Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Progressive Hydration #react_fukuoka
Hiroyuki ANAI
June 05, 2019
Programming
5
1.2k
Progressive Hydration #react_fukuoka
Hiroyuki ANAI
June 05, 2019
Tweet
Share
More Decks by Hiroyuki ANAI
See All by Hiroyuki ANAI
Step Functionsの設計時に知っておいたほうがいいかもしれないこと
pirosikick
0
160
Go言語による並行処理「4.4 orチャネル」の図
pirosikick
0
190
サイボウズWebフロントエンド脱レガシーの今までとこれから
pirosikick
5
15k
@cybozu/eslint-configから学ぶ、全社共通ESLint configの運用
pirosikick
4
1.4k
Web Share Target API #w3fukuoka
pirosikick
0
480
Google I/O '19のWebをまとめる会
pirosikick
2
670
PuppeteerでいらないCSSを消す
pirosikick
24
25k
WebFEのテストにおける気持ちの変遷
pirosikick
0
320
私が考えるReactのよさ #fukuokajs
pirosikick
2
610
Other Decks in Programming
See All in Programming
フロントエンドで 良いコードを書くために
t_keshi
3
1.6k
Hono v3 - Do Everything, Run Anywhere, But Small, And Faster
yusukebe
4
130
Milestoner
bkuhlmann
1
240
ECS Service Connectでマイクロサービスを繋いでみた
xblood
0
550
AWSとCPUのムフフな関係
cmdemura
0
470
Hasura の Relationship と権限管理
karszawa
0
170
10年以上続くプロダクトの フロントエンド刷新プロジェクトのふりかえり
yotahada3
2
340
Refactor with using `available` and `deprecated`
417_72ki
3
380
爆速の日経電子版開発の今
shinyaigeek
2
620
Remote SSHで行うVS Codeリモートホスト開発とトラブルシューティング
smt7174
1
470
Git Rebase
bkuhlmann
10
1.2k
監視せなあかんし、五大紙だけにオオカミってな🐺🐺🐺🐺🐺
sadnessojisan
2
1.5k
Featured
See All Featured
Three Pipe Problems
jasonvnalue
89
8.9k
Into the Great Unknown - MozCon
thekraken
2
290
Making Projects Easy
brettharned
102
4.8k
Unsuck your backbone
ammeep
659
56k
Why You Should Never Use an ORM
jnunemaker
PRO
49
7.9k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
31
20k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
32
6.7k
Raft: Consensus for Rubyists
vanstee
130
5.7k
Bootstrapping a Software Product
garrettdimon
299
110k
10 Git Anti Patterns You Should be Aware of
lemiorhan
643
54k
Testing 201, or: Great Expectations
jmmastey
25
5.7k
Six Lessons from altMBA
skipperchong
15
2.3k
Transcript
1SPHSFTTJWF )ZESBUJPO !QJSPTJLJDL ʢ8FEʣ 3FBDUษڧձ!ԬWPM
w !QJSPTJLJDL w αΠϘζגࣜձࣾ ϑϩϯτΤϯυΤΩεύʔτνʔϜ w స৬͠·ͨ͠! ԬͷΤϯδχΞ·ͩҰਓͰ͕͢ ͨͷ͍͠৬Ͱ͢ w
ԬͰ࠾༻ͯ͠·͢ ࣗݾհ
͢͜ͱ w )ZESBUJPOͷ͓͞Β͍ w 1SPHSFTTJWF)ZESBUJPOʹ͍ͭͯ
)ZESBUJPOͷ͓͞Β͍
)ZESBUJPOͱʁ w 4FSWFS4JEF3FOEFSJOHʢҎԼɺ443ʣʹΑͬͯ ඳը͞ΕͨίϯςϯπΛɺ w $MJFOU4JEF3FOEFSJOHʢҎԼɺ$43ʣͰ͍ճ͢͜ͱ
$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') );
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); });
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') );
$41POMZ )551(&5 +4ͷμϯϩʔυ +4ͷ࣮ߦ '$1 +4͕࣮ߦ͞ΕΔ·Ͱը໘͕ਅͬന '$1'JSTU$POUFOUGVM1BJOUɺ ϖʔδભҠ͔Β࠷ॳͷίϯςϯπඳը·Ͱͷ࣌ؒ
443 )ZESBUJPO )551(&5 +4ͷμϯϩʔυ +4ͷ࣮ߦ '$1 '$1 '$1͕͘ͳΔ 443Ͱ ඳը͞ΕΔ
͜͜·Ͱͷ·ͱΊ w )ZESBUJPO443ͷ݁ՌΛ$43Ͱ͍ճ͢͜ͱΛݴ͏ w 443ʴ)ZESBUJPOʹΑͬͯ'$1͕͘ͳΔ w αʔόʔαΠυͰ 3FBDU%0.4FSWFSSFOEFS5P4USJOH͔ 3FBDU%0.4FSWFSSFOEFS5P/PEF4USFBNΛ͏ w
ΫϥΠΞϯταΠυͰ 3FBDU%0.SFOEFSͰͳ͘3FBDU%0.IZESBUFΛ͏
1SPHSFTTJWF )ZESBUJPO ʹ͍ͭͯ
)ZESBUJPOͷ w '$1͘ͳΔ͕55*͘ͳΒͳ͍ w 55*5JNF5P*OUFSBDUJWF w ϖʔδ͕ΠϯλϥΫςΟϒʢૢ࡞Մೳʣʹ ͳΔ·Ͱͷ࣌ؒ w ඳը͞Εͯૢ࡞ՄೳʹͳΔͷɺ+4͕࣮ߦ͞Εͨޙ
$41POMZ )551(&5 +4ͷμϯϩʔυ +4ͷ࣮ߦ '$1 55*
443 )ZESBUJPO )551(&5 +4ͷμϯϩʔυ +4ͷ࣮ߦ '$1 55*
1SPHSFTTJWF)ZESBUJPO w )ZESBUJPOΛஈ֊తʹߦ͏͜ͱͰ55*Λॖ͢Δ w (PPHMF*0Ͱհ͞ΕͨςΫχοΫ w 3FOEFSJOHPOUIF8FC 1FSGPSNBODF*NQMJDBUJPOTPG"QQMJDBUJPO"SDIJUFDUVSF IUUQTZPVUVCFL"7GV630H
443 )ZESBUJPO )551(&5 +4ͷ%- +4ͷ࣮ߦ '$1 55* Ұؾʹ)ZESBUJPO
1SPHSFTTJWF)ZESBUJPO +4ͷ࣮ߦ 55* ඞཁͳ͚ͩ)ZESBUJPO )551(&5 +4ͷ%- '$1 +4ͷ࣮ߦ Γͷ)ZESBUJPO ҰؾʹΔΑΓ
͘ͳΔ ඞཁʹͳͬͨΒ )ZESBUJPO͢Δ ɾɾɾ
σϞ w (PPHMF͕σϞΛެ։͍ͯ͠Δ w IUUQTHJUIVCDPN(PPHMF$ISPNF-BCTQSPHSFTTJWF SFOEFSJOHGSBNFXPSLTTBNQMFT w ϑΝʔετϏϡʔͷޙʹ݅ͷϦετ͕͋Δϖʔδ w Ϧετ෦࠷ॳͷ)ZESBUJPOͷର֎
w Ϧετ෦ը໘ʹೖͬͨλΠϛϯάͰ )ZESBUJPO͞ΕΔ
ϑΝʔετϏϡʔ ࠷ॳʹ)ZESBUJPO͞ΕΔ Ϧετ෦ ը໘ʹೖͬͨΒ)ZESBUJPO͞ΕΔ
"VEJUTͰൺֱ 1)ͳ͠ 1)͋Γ 55*͕ඵߴʹ
αϯϓϧͷιʔείʔυΛ ಡΉ w BQQKT w ϧʔτίϯϙʔωϯτ͕ఆٛ͞Ε͍ͯΔ w TUSFBNKTʢ˞ࠓճಡ·ͳ͍ʣ w Ϧετ෦ͷ4USFBNίϯϙʔωϯτ͕ఆٛ͞Ε͍ͯΔ
w IZESBUPSKT w 1SPHSFTTJWF)ZESBUJPOΛ࣮ߦ͢Δίϯϙʔωϯτ
// 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ʣ
// 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͕͔Εɺ ಈతʹϒϥβʹϩʔυ͞ΕΔ
// 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ίϯϙʔωϯτΛಡΈࠐΉ
// 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͞ΕΔ
// 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
// 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Λฦ͍ͯ͠ΔͷͰ Ξοϓσʔτ͞Εͳ͍
// 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Λᷖճ͢Δ
// 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Λ࣮ߦ
w αʔόʔαΠυී௨ʹ443ΛΔ w )ZESBUPSίϯϙʔωϯτͷԼ ࠷ॳͷ)ZESBUJPOΛᷖճ͢Δ w EBOHFSPVTMZ4FU*OOFS)5.-ʹۭจࣈΛ͢ w )ZESBUPSίϯϙʔωϯτͷԼ͕ ը໘ʹೖͬͨΒͦͷ෦ͷ)ZESBUJPOΛ࣮ߦ
w *OUFSTFDUJPO0CTFSWFSͰࢹ͠ɺ 3FBDU%0.IZESBUFΛ࣮ߦ
·ͱΊ
w 1SPHSFTTJWF)ZESBUJPO ஈ֊తʹ)ZESBUJPO͢Δ͜ͱͰ55*Λॖ͢Δ w ݱঢ়)BDLZͳ࣮͕ͩɺެࣜʹରԠ༧ఆ͕͋Δ w "OHVMBSɺ7VFͰͰ͖ΔʢΒ͍͠ʣ
1SPHSFTTJWF)ZESBUJPO ಋೖ͖͔͢ʁ w ಋೖʹϦεΫ͕΄΅ͳ͍ w )ZESBUPSίϯϙʔωϯτΛऔΓআ͚ͩ͘ͰݩʹͤΔ w ݱঢ়ϋοΫͬΆ͍࣮ɻόʔδϣϯΞοϓ࣌ʹҙ͕ඞཁ͔ w 55*͕ಛผ͍ॴͰಋೖ͢Δͷ͕Αͦ͞͏
w 55*͕͍ॴͰޮՌ͕ബ͍ͷͰɺ શϖʔδͰಋೖ͖͔͢ඍົ w "VEJUTͰܭଌ͠·͠ΐ͏
w (PPHMF*0ͷ8FCΛ·ͱΊΔձ IUUQTNFOUBJDPKTDPOOQBTTDPNFWFOU w ʢਫʣ w ·ۭ͍ͩͯ·͢ͷͰɺͥͻʙ
͋Γ͕ͱ͏ ͍͟͝·ͨ͠