Progressive Hydration #react_fukuoka

Progressive Hydration #react_fukuoka

Dc52871050369929247b69f3767e8737?s=128

Hiroyuki ANAI

June 05, 2019
Tweet

Transcript

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

  2. w !QJSPTJLJDL w αΠϘ΢ζגࣜձࣾ
 ϑϩϯτΤϯυΤΩεύʔτνʔϜ w స৬͠·ͨ͠!
 ෱ԬͷΤϯδχΞ͸·ͩҰਓͰ͕͢
 ͨͷ͍͠৬৔Ͱ͢ w

    ෱ԬͰ΋࠾༻ͯ͠·͢ ࣗݾ঺հ
  3. ࿩͢͜ͱ w )ZESBUJPOͷ͓͞Β͍ w 1SPHSFTTJWF)ZESBUJPOʹ͍ͭͯ

  4. )ZESBUJPOͷ͓͞Β͍

  5. )ZESBUJPOͱ͸ʁ w 4FSWFS4JEF3FOEFSJOHʢҎԼɺ443ʣʹΑͬͯ
 ඳը͞ΕͨίϯςϯπΛɺ w $MJFOU4JEF3FOEFSJOHʢҎԼɺ$43ʣͰ࢖͍ճ͢͜ͱ

  6. $43POMZ w ίϯϙʔωϯτͷπϦʔ͔Β%0.Λੜ੒ɺඳը w Πϕϯτϋϯυϥʔͷઃఆ // client.js import React from

    'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render( <App />, document.getElementById('root') );
  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><App /></Html> ); html.pipe(res); });
  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( <App />, document.getElementById('root') );
  9. $41POMZ )551(&5 +4ͷμ΢ϯϩʔυ +4ͷ࣮ߦ '$1 +4͕࣮ߦ͞ΕΔ·Ͱը໘͕ਅͬന '$1'JSTU$POUFOUGVM1BJOUɺ ϖʔδભҠ͔Β࠷ॳͷίϯςϯπඳը·Ͱͷ࣌ؒ

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

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

    ΫϥΠΞϯταΠυͰ͸
 3FBDU%0.SFOEFSͰ͸ͳ͘3FBDU%0.IZESBUFΛ࢖͏
  12. 1SPHSFTTJWF )ZESBUJPO ʹ͍ͭͯ

  13. )ZESBUJPOͷ໰୊఺ w '$1͸଎͘ͳΔ͕55*͸଎͘ͳΒͳ͍ w 55*5JNF5P*OUFSBDUJWF w ϖʔδ͕ΠϯλϥΫςΟϒʢૢ࡞Մೳʣʹ
 ͳΔ·Ͱͷ࣌ؒ w ඳը͞Εͯ΋ૢ࡞ՄೳʹͳΔͷ͸ɺ+4͕࣮ߦ͞Εͨޙ

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

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

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

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

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

    ଎͘ͳΔ ඞཁʹͳͬͨΒ )ZESBUJPO͢Δ ɾɾɾ
  19. σϞ w (PPHMF͕σϞΛެ։͍ͯ͠Δ w IUUQTHJUIVCDPN(PPHMF$ISPNF-BCTQSPHSFTTJWF SFOEFSJOHGSBNFXPSLTTBNQMFT w ϑΝʔετϏϡʔͷޙʹ݅ͷϦετ͕͋Δϖʔδ w Ϧετ෦෼͸࠷ॳͷ)ZESBUJPOͷର৅֎

    w Ϧετ෦෼͸ը໘಺ʹೖͬͨλΠϛϯάͰ
 )ZESBUJPO͞ΕΔ
  20. ϑΝʔετϏϡʔ ࠷ॳʹ)ZESBUJPO͞ΕΔ Ϧετ෦෼ ը໘಺ʹೖͬͨΒ)ZESBUJPO͞ΕΔ

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

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

    w IZESBUPSKT w 1SPHSFTTJWF)ZESBUJPOΛ࣮ߦ͢Δίϯϙʔωϯτ
  23. // app.js … let load = () => import('./stream'); let

    Hydrator = ClientHydrator; if (typeof window === 'undefined') { Hydrator = ServerHydrator; load = () => require('./stream'); } export default function App() { return ( <div id="app"> <Header /> <Intro /> <Hydrator load={load} /> </div> ); } BQQKT ϧʔτίϯϙʔωϯτʢ"QQʣ
  24. // app.js … let load = () => import('./stream'); let

    Hydrator = ClientHydrator; if (typeof window === 'undefined') { Hydrator = ServerHydrator; load = () => require('./stream'); } export default function App() { return ( <div id="app"> <Header /> <Intro /> <Hydrator load={load} /> </div> ); } $43Ͱ͸%ZOBNJD*NQPSUͰ 4USFBNίϯϙʔωϯτΛಡΈࠐΉ 4USFBNίϯϙʔωϯτ͸ #VOEMF͕෼͔Εɺ ಈతʹϒϥ΢βʹϩʔυ͞ΕΔ
  25. // app.js … let load = () => import('./stream'); let

    Hydrator = ClientHydrator; if (typeof window === 'undefined') { Hydrator = ServerHydrator; load = () => require('./stream'); } export default function App() { return ( <div id="app"> <Header /> <Intro /> <Hydrator load={load} /> </div> ); } 443࣌͸੩తʹ 4USFBNίϯϙʔωϯτΛಡΈࠐΉ
  26. // app.js … let load = () => import('./stream'); let

    Hydrator = ClientHydrator; if (typeof window === 'undefined') { Hydrator = ServerHydrator; load = () => require('./stream'); } export default function App() { return ( <div id="app"> <Header /> <Intro /> <Hydrator load={load} /> </div> ); } )ZESBUPSίϯϙʔωϯτ͕ 1SPHSFTTJWF)ZESBUJPOΛ࣮ߦ͢Δ QSPQTMPBE͕ฦ͢ίϯϙʔωϯτ͸ ஗Ԇͯ͠)ZESBUJPO͞ΕΔ
  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(<Child {...props} />, this.root); }).observe(this.root); } render() { return ( <section ref={c => this.root = c} dangerouslySetInnerHTML={{ __html: '' }} suppressHydrationWarning /> ); } } IZESBUPSKT )ZESBUPS$MJFOU)ZESBUPS
  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(<Child {...props} />, this.root); }).observe(this.root); } render() { return ( <section ref={c => this.root = c} dangerouslySetInnerHTML={{ __html: '' }} suppressHydrationWarning /> ); } } w TIPVME$PNQPOFOU6QEBUFͰ
 ৗʹGBMTFΛฦ͍ͯ͠ΔͷͰ
 Ξοϓσʔτ͞Εͳ͍
  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(<Child {...props} />, this.root); }).observe(this.root); } render() { return ( <section ref={c => this.root = c} dangerouslySetInnerHTML={{ __html: '' }} suppressHydrationWarning /> ); } } w ۭͷཁૉΛग़ྗɺ
 ཁૉͷSFGΛऔಘ w EBOHFSPVTMZ4FU*OOFS)5.-Ͱ
 ۭจࣈΛઃఆ͢Δͱɺ
 )ZESBUJPOΛᷖճ͢Δ
  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(<Child {...props} />, this.root); }).observe(this.root); } render() { return ( <section ref={c => this.root = c} dangerouslySetInnerHTML={{ __html: '' }} suppressHydrationWarning /> ); } } w *OUFSTFDUJPO0CTFSWFSͰ
 ཁૉͷεΫϩʔϧҐஔΛ؂ࢹ w ཁૉ͕ը໘಺ʹೖͬͨΒɺ
 )ZESBUJPOΛ࣮ߦ
  31. w αʔόʔαΠυ͸ී௨ʹ443Λ΍Δ w )ZESBUPSίϯϙʔωϯτͷ഑Լ͸
 ࠷ॳͷ)ZESBUJPOΛᷖճ͢Δ w EBOHFSPVTMZ4FU*OOFS)5.-ʹۭจࣈΛ౉͢ w )ZESBUPSίϯϙʔωϯτͷ഑Լ͕
 ը໘಺ʹೖͬͨΒͦͷ෦෼ͷ)ZESBUJPOΛ࣮ߦ

    w *OUFSTFDUJPO0CTFSWFSͰ؂ࢹ͠ɺ
 3FBDU%0.IZESBUFΛ࣮ߦ
  32. ·ͱΊ

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

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

    w 55*͕଎͍৔ॴͰ͸ޮՌ͕ബ͍ͷͰɺ
 શϖʔδͰಋೖ͢΂͖͔͸ඍົ w "VEJUTͰܭଌ͠·͠ΐ͏
  35. w (PPHMF*0ͷ8FCΛ·ͱΊΔձ
 IUUQTNFOUBJDPKTDPOOQBTTDPNFWFOU w ʢਫʣ w ·ۭ͍ͩͯ·͢ͷͰɺͥͻʙ

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