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
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
420
fukuoka.ts #3 社内でESLintの共通設定を配りたい2025年春版
pirosikick
3
480
compilerOptions、全部読んだ
pirosikick
1
300
Step Functionsの設計時に知っておいたほうがいいかもしれないこと
pirosikick
0
530
Go言語による並行処理「4.4 orチャネル」の図
pirosikick
0
480
サイボウズ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
890
Other Decks in Programming
See All in Programming
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
440
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
170
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
520
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
540
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
2.6k
継続モナドとリアクティブプログラミング
yukikurage
3
600
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
930
作るコストが小さくなった時代 幸せに働くために改めて考えたいこと 〜エンジニアとして価値を出し続けるために注視している二分野〜
yuppeeng
0
100
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
370
共通化で考えるべきは、実装より公開する型だった
codeegg
0
240
関数型プログラミングのメリットって何だろう?
wanko_it
0
180
AIエージェントで 変わるAndroid開発環境
takahirom
2
660
Featured
See All Featured
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.3k
Designing for humans not robots
tammielis
254
26k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
Producing Creativity
orderedlist
PRO
348
40k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.1k
A Tale of Four Properties
chriscoyier
163
24k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
300
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.4k
Facilitating Awesome Meetings
lara
57
7k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
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 ·ۭ͍ͩͯ·͢ͷͰɺͥͻʙ
͋Γ͕ͱ͏ ͍͟͝·ͨ͠