Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Progressive Hydration #react_fukuoka
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Hiroyuki ANAI
June 05, 2019
Programming
1.9k
6
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Progressive Hydration #react_fukuoka
Hiroyuki ANAI
June 05, 2019
More Decks by Hiroyuki ANAI
See All by Hiroyuki ANAI
書き換えて学ぶTemporal #fukts
pirosikick
2
430
fukuoka.ts #3 社内でESLintの共通設定を配りたい2025年春版
pirosikick
3
500
compilerOptions、全部読んだ
pirosikick
1
300
Step Functionsの設計時に知っておいたほうがいいかもしれないこと
pirosikick
0
550
Go言語による並行処理「4.4 orチャネル」の図
pirosikick
0
500
サイボウズWebフロントエンド脱レガシーの今までとこれから
pirosikick
6
17k
@cybozu/eslint-configから学ぶ、全社共通ESLint configの運用
pirosikick
4
1.9k
Web Share Target API #w3fukuoka
pirosikick
0
740
Google I/O '19のWebをまとめる会
pirosikick
2
900
Other Decks in Programming
See All in Programming
Can LLMs Replicate 4 Years of Compose Migration? Exploring the boundaries of automation with 279 XML files from a real product
makun
0
130
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.5k
業務時間外もAIに働いてもらう話
colorful12
3
10k
tsc.rip を支える技術 / Kyoto.なんか #8
susisu
0
4.3k
新卒PdEのリアル
ryu1013
1
420
型解析で実現する Go の言語内 DSL / Conference に Go! タイムテーブルの歩き方 for Gophers
mazrean
0
130
Oxlintはいいぞ(続)
yug1224
1
550
片田舎のおっさん、 Swift Buildのダイアモンド問題解決の不具合修正PRを出すが、解決方法がキャッシュをしないようにすることであり、ビルド時間が伸びると言われてマージされないので高速化もする/swiftbuild
yimajo
0
360
AWS Step Functions 大規模並列の壁を越える / jaws-sonic-2026-niigata-step-functions
kasacchiful
PRO
0
380
高専キャリア LT 発表内容
crysta1221
6
5.6k
FastAPI の並行処理モデルを完全に理解する
hoto17296
9
3.8k
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
220
Featured
See All Featured
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
320
A Modern Web Designer's Workflow
chriscoyier
698
190k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Into the Great Unknown - MozCon
thekraken
41
2.7k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
45k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.6k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
Being A Developer After 40
akosma
91
590k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6.2k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
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 ·ۭ͍ͩͯ·͢ͷͰɺͥͻʙ
͋Γ͕ͱ͏ ͍͟͝·ͨ͠