$30 off During Our Annual Pro Sale. View Details »

Progressive Hydration #react_fukuoka

Progressive Hydration #react_fukuoka

Hiroyuki ANAI

June 05, 2019
Tweet

More Decks by Hiroyuki ANAI

Other Decks in Programming

Transcript

  1. 1SPHSFTTJWF
    )ZESBUJPO
    !QJSPTJLJDL
    ʢ8FEʣ
    3FBDUษڧձ!෱ԬWPM

    View Slide

  2. w !QJSPTJLJDL
    w αΠϘ΢ζגࣜձࣾ

    ϑϩϯτΤϯυΤΩεύʔτνʔϜ
    w స৬͠·ͨ͠!

    ෱ԬͷΤϯδχΞ͸·ͩҰਓͰ͕͢

    ͨͷ͍͠৬৔Ͱ͢
    w ෱ԬͰ΋࠾༻ͯ͠·͢
    ࣗݾ঺հ

    View Slide

  3. ࿩͢͜ͱ
    w )ZESBUJPOͷ͓͞Β͍
    w 1SPHSFTTJWF)ZESBUJPOʹ͍ͭͯ

    View Slide

  4. )ZESBUJPOͷ͓͞Β͍

    View Slide

  5. )ZESBUJPOͱ͸ʁ
    w 4FSWFS4JEF3FOEFSJOHʢҎԼɺ443ʣʹΑͬͯ

    ඳը͞ΕͨίϯςϯπΛɺ
    w $MJFOU4JEF3FOEFSJOHʢҎԼɺ$43ʣͰ࢖͍ճ͢͜ͱ

    View Slide

  6. $43POMZ
    w ίϯϙʔωϯτͷπϦʔ͔Β%0.Λੜ੒ɺඳը
    w Πϕϯτϋϯυϥʔͷઃఆ
    // client.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    ReactDOM.render(
    ,
    document.getElementById('root')
    );

    View Slide

  7. 443)ZESBUJPO
    w αʔόʔαΠυ
    w ίϯϙʔωϯτπϦʔΛ)5.-ͷจࣈྻʹม׵͠ɺϨεϙϯε͢Δ
    • ReactDOMServer.{renderToString|renderToNodeStream}
    // server.js
    const ReactDOMServer = require('react-dom/server');
    …省略
    app.get('/', (req, res) => {
    const html = ReactDOMServer.renderToNodeStream(

    );
    html.pipe(res);
    });

    View Slide

  8. 443)ZESBUJPO
    w ΫϥΠΞϯταΠυ
    w 443͕ฦ͢)5.-Ͱ%0.͕ߏஙɾඳըࡁΈ
    w ΠϕϯτϋϯυϥʔͷઃఆͷΈΛߦ͏
    w 3FBDU%0.SFOEFSͰ͸ͳ͘ɺ3FBDU%0.IZESBUFΛ࢖͏
    // client.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    ReactDOM.hydrate(
    ,
    document.getElementById('root')
    );

    View Slide

  9. $41POMZ
    )551(&5
    +4ͷμ΢ϯϩʔυ
    +4ͷ࣮ߦ
    '$1
    +4͕࣮ߦ͞ΕΔ·Ͱը໘͕ਅͬന
    '$1'JSTU$POUFOUGVM1BJOUɺ
    ϖʔδભҠ͔Β࠷ॳͷίϯςϯπඳը·Ͱͷ࣌ؒ

    View Slide

  10. 443)ZESBUJPO
    )551(&5
    +4ͷμ΢ϯϩʔυ
    +4ͷ࣮ߦ
    '$1 '$1
    '$1͕଎͘ͳΔ
    443Ͱ
    ඳը͞ΕΔ

    View Slide

  11. ͜͜·Ͱͷ·ͱΊ
    w )ZESBUJPO͸443ͷ݁ՌΛ$43Ͱ࢖͍ճ͢͜ͱΛݴ͏
    w 443ʴ)ZESBUJPOʹΑͬͯ'$1͕଎͘ͳΔ
    w αʔόʔαΠυͰ͸

    3FBDU%0.4FSWFSSFOEFS5P4USJOH͔
    3FBDU%0.4FSWFSSFOEFS5P/PEF4USFBNΛ࢖͏
    w ΫϥΠΞϯταΠυͰ͸

    3FBDU%0.SFOEFSͰ͸ͳ͘3FBDU%0.IZESBUFΛ࢖͏

    View Slide

  12. 1SPHSFTTJWF
    )ZESBUJPO
    ʹ͍ͭͯ

    View Slide

  13. )ZESBUJPOͷ໰୊఺
    w '$1͸଎͘ͳΔ͕55*͸଎͘ͳΒͳ͍
    w 55*5JNF5P*OUFSBDUJWF
    w ϖʔδ͕ΠϯλϥΫςΟϒʢૢ࡞Մೳʣʹ

    ͳΔ·Ͱͷ࣌ؒ
    w ඳը͞Εͯ΋ૢ࡞ՄೳʹͳΔͷ͸ɺ+4͕࣮ߦ͞Εͨޙ

    View Slide

  14. $41POMZ
    )551(&5
    +4ͷμ΢ϯϩʔυ
    +4ͷ࣮ߦ
    '$1
    55*

    View Slide

  15. 443)ZESBUJPO
    )551(&5
    +4ͷμ΢ϯϩʔυ
    +4ͷ࣮ߦ
    '$1
    55*

    View Slide

  16. 1SPHSFTTJWF)ZESBUJPO
    w )ZESBUJPOΛஈ֊తʹߦ͏͜ͱͰ55*Λ୹ॖ͢Δ
    w (PPHMF*0Ͱ঺հ͞ΕͨςΫχοΫ
    w 3FOEFSJOHPOUIF8FC

    1FSGPSNBODF*NQMJDBUJPOTPG"QQMJDBUJPO"SDIJUFDUVSF

    IUUQTZPVUVCFL"7GV630H

    View Slide

  17. 443)ZESBUJPO
    )551(&5
    +4ͷ%-
    +4ͷ࣮ߦ
    '$1
    55*
    Ұؾʹ)ZESBUJPO

    View Slide

  18. 1SPHSFTTJWF)ZESBUJPO
    +4ͷ࣮ߦ
    55*
    ඞཁͳ෼͚ͩ)ZESBUJPO
    )551(&5
    +4ͷ%-
    '$1
    +4ͷ࣮ߦ
    ࢒Γͷ)ZESBUJPO
    Ұؾʹ΍ΔΑΓ
    ଎͘ͳΔ
    ඞཁʹͳͬͨΒ
    )ZESBUJPO͢Δ
    ɾɾɾ

    View Slide

  19. σϞ
    w (PPHMF͕σϞΛެ։͍ͯ͠Δ
    w IUUQTHJUIVCDPN(PPHMF$ISPNF-BCTQSPHSFTTJWF
    SFOEFSJOHGSBNFXPSLTTBNQMFT
    w ϑΝʔετϏϡʔͷޙʹ݅ͷϦετ͕͋Δϖʔδ
    w Ϧετ෦෼͸࠷ॳͷ)ZESBUJPOͷର৅֎
    w Ϧετ෦෼͸ը໘಺ʹೖͬͨλΠϛϯάͰ

    )ZESBUJPO͞ΕΔ

    View Slide

  20. ϑΝʔετϏϡʔ
    ࠷ॳʹ)ZESBUJPO͞ΕΔ
    Ϧετ෦෼
    ը໘಺ʹೖͬͨΒ)ZESBUJPO͞ΕΔ

    View Slide

  21. "VEJUTͰൺֱ
    1)ͳ͠
    1)͋Γ
    55*͕ඵߴ଎ʹ

    View Slide

  22. αϯϓϧͷιʔείʔυΛ
    ಡΉ
    w BQQKT
    w ϧʔτίϯϙʔωϯτ͕ఆٛ͞Ε͍ͯΔ
    w TUSFBNKTʢ˞ࠓճ͸ಡ·ͳ͍ʣ
    w Ϧετ෦෼ͷ4USFBNίϯϙʔωϯτ͕ఆٛ͞Ε͍ͯΔ
    w IZESBUPSKT
    w 1SPHSFTTJWF)ZESBUJPOΛ࣮ߦ͢Δίϯϙʔωϯτ

    View Slide

  23. // app.js

    let load = () => import('./stream');
    let Hydrator = ClientHydrator;
    if (typeof window === 'undefined') {
    Hydrator = ServerHydrator;
    load = () => require('./stream');
    }
    export default function App() {
    return (





    );
    }
    BQQKT
    ϧʔτίϯϙʔωϯτʢ"QQʣ

    View Slide

  24. // app.js

    let load = () => import('./stream');
    let Hydrator = ClientHydrator;
    if (typeof window === 'undefined') {
    Hydrator = ServerHydrator;
    load = () => require('./stream');
    }
    export default function App() {
    return (





    );
    }
    $43Ͱ͸%ZOBNJD*NQPSUͰ
    4USFBNίϯϙʔωϯτΛಡΈࠐΉ
    4USFBNίϯϙʔωϯτ͸
    #VOEMF͕෼͔Εɺ
    ಈతʹϒϥ΢βʹϩʔυ͞ΕΔ

    View Slide

  25. // app.js

    let load = () => import('./stream');
    let Hydrator = ClientHydrator;
    if (typeof window === 'undefined') {
    Hydrator = ServerHydrator;
    load = () => require('./stream');
    }
    export default function App() {
    return (





    );
    }
    443࣌͸੩తʹ
    4USFBNίϯϙʔωϯτΛಡΈࠐΉ

    View Slide

  26. // app.js

    let load = () => import('./stream');
    let Hydrator = ClientHydrator;
    if (typeof window === 'undefined') {
    Hydrator = ServerHydrator;
    load = () => require('./stream');
    }
    export default function App() {
    return (





    );
    }
    )ZESBUPSίϯϙʔωϯτ͕
    1SPHSFTTJWF)ZESBUJPOΛ࣮ߦ͢Δ
    QSPQTMPBE͕ฦ͢ίϯϙʔωϯτ͸
    ஗Ԇͯ͠)ZESBUJPO͞ΕΔ

    View Slide

  27. // hydrator.js

    export class Hydrator extends React.Component {
    shouldComponentUpdate() {
    return false;
    }
    componentDidMount() {
    new IntersectionObserver(async ([entry], obs) => {
    if (!entry.isIntersecting) return;
    obs.unobserve(this.root);
    const { load, ...props } = this.props;
    const Child = interopDefault(await load());
    ReactDOM.hydrate(, this.root);
    }).observe(this.root);
    }
    render() {
    return (
    ref={c => this.root = c}
    dangerouslySetInnerHTML={{ __html: '' }}
    suppressHydrationWarning
    />
    );
    }
    }
    IZESBUPSKT
    )ZESBUPS$MJFOU)ZESBUPS

    View Slide

  28. // hydrator.js

    export class Hydrator extends React.Component {
    shouldComponentUpdate() {
    return false;
    }
    componentDidMount() {
    new IntersectionObserver(async ([entry], obs) => {
    if (!entry.isIntersecting) return;
    obs.unobserve(this.root);
    const { load, ...props } = this.props;
    const Child = interopDefault(await load());
    ReactDOM.hydrate(, this.root);
    }).observe(this.root);
    }
    render() {
    return (
    ref={c => this.root = c}
    dangerouslySetInnerHTML={{ __html: '' }}
    suppressHydrationWarning
    />
    );
    }
    }
    w TIPVME$PNQPOFOU6QEBUFͰ

    ৗʹGBMTFΛฦ͍ͯ͠ΔͷͰ

    Ξοϓσʔτ͞Εͳ͍

    View Slide

  29. // hydrator.js

    export class Hydrator extends React.Component {
    shouldComponentUpdate() {
    return false;
    }
    componentDidMount() {
    new IntersectionObserver(async ([entry], obs) => {
    if (!entry.isIntersecting) return;
    obs.unobserve(this.root);
    const { load, ...props } = this.props;
    const Child = interopDefault(await load());
    ReactDOM.hydrate(, this.root);
    }).observe(this.root);
    }
    render() {
    return (
    ref={c => this.root = c}
    dangerouslySetInnerHTML={{ __html: '' }}
    suppressHydrationWarning
    />
    );
    }
    }
    w ۭͷཁૉΛग़ྗɺ

    ཁૉͷSFGΛऔಘ
    w EBOHFSPVTMZ4FU*OOFS)5.-Ͱ

    ۭจࣈΛઃఆ͢Δͱɺ

    )ZESBUJPOΛᷖճ͢Δ

    View Slide

  30. // hydrator.js

    export class Hydrator extends React.Component {
    shouldComponentUpdate() {
    return false;
    }
    componentDidMount() {
    new IntersectionObserver(async ([entry], obs) => {
    if (!entry.isIntersecting) return;
    obs.unobserve(this.root);
    const { load, ...props } = this.props;
    const Child = interopDefault(await load());
    ReactDOM.hydrate(, this.root);
    }).observe(this.root);
    }
    render() {
    return (
    ref={c => this.root = c}
    dangerouslySetInnerHTML={{ __html: '' }}
    suppressHydrationWarning
    />
    );
    }
    }
    w *OUFSTFDUJPO0CTFSWFSͰ

    ཁૉͷεΫϩʔϧҐஔΛ؂ࢹ
    w ཁૉ͕ը໘಺ʹೖͬͨΒɺ

    )ZESBUJPOΛ࣮ߦ

    View Slide

  31. w αʔόʔαΠυ͸ී௨ʹ443Λ΍Δ
    w )ZESBUPSίϯϙʔωϯτͷ഑Լ͸

    ࠷ॳͷ)ZESBUJPOΛᷖճ͢Δ
    w EBOHFSPVTMZ4FU*OOFS)5.-ʹۭจࣈΛ౉͢
    w )ZESBUPSίϯϙʔωϯτͷ഑Լ͕

    ը໘಺ʹೖͬͨΒͦͷ෦෼ͷ)ZESBUJPOΛ࣮ߦ
    w *OUFSTFDUJPO0CTFSWFSͰ؂ࢹ͠ɺ

    3FBDU%0.IZESBUFΛ࣮ߦ

    View Slide

  32. ·ͱΊ

    View Slide

  33. w 1SPHSFTTJWF)ZESBUJPO͸

    ஈ֊తʹ)ZESBUJPO͢Δ͜ͱͰ55*Λ୹ॖ͢Δ
    w ݱঢ়͸)BDLZͳ࣮૷͕ͩɺެࣜʹରԠ༧ఆ͕͋Δ
    w "OHVMBSɺ7VFͰ΋Ͱ͖ΔʢΒ͍͠ʣ

    View Slide

  34. 1SPHSFTTJWF)ZESBUJPO͸
    ಋೖ͢΂͖͔ʁ
    w ಋೖʹϦεΫ͕΄΅ͳ͍
    w )ZESBUPSίϯϙʔωϯτΛऔΓআ͚ͩ͘Ͱݩʹ໭ͤΔ
    w ݱঢ়͸ϋοΫͬΆ͍࣮૷ɻόʔδϣϯΞοϓ࣌ʹ஫ҙ͕ඞཁ͔΋
    w 55*͕ಛผ஗͍৔ॴͰಋೖ͢Δͷ͕Αͦ͞͏
    w 55*͕଎͍৔ॴͰ͸ޮՌ͕ബ͍ͷͰɺ

    શϖʔδͰಋೖ͢΂͖͔͸ඍົ
    w "VEJUTͰܭଌ͠·͠ΐ͏

    View Slide

  35. w (PPHMF*0ͷ8FCΛ·ͱΊΔձ

    IUUQTNFOUBJDPKTDPOOQBTTDPNFWFOU
    w ʢਫʣ
    w ·ۭ͍ͩͯ·͢ͷͰɺͥͻʙ

    View Slide

  36. ͋Γ͕ͱ͏
    ͍͟͝·ͨ͠

    View Slide