React Server Components で SSR する場合の Hydration について調べてみた
React Server Components (RSC) について学んでいる中で、Server Component (SC) を SSR した場合に Hydration はどうなるんだろう?と疑問に思ったので調べてみたメモ。間違っていたらすいません。
これまでの Hydration
クライアント側の JS に hydrate すべきコンポーネントが存在するので、普通にhydrateRoot(dom, <App />)
SC の Hydration
SC の JS はクライアントに配信されない。これによってクライアント側のコード量が減るのが RSC の利点の1つ。
では hydrateRoot(dom, <App />)
するための <App>
をどこから持ってくるか?
Next.jsが出てこないReact Server Componentsハンズオン の クライアントサイドの実装 では、RSC のプロトコルを HTML に埋め込み、それを利用して <App>
を組み立てていた。
なるほどーと思いつつ、一応 Next.js 13 を利用して RSC を使う場合と使わない場合に生成される HTML や処理を比較してみた。
Next.js における Hydration の比較
Next.js 13 app(beta) の Hydration
Next.js 13 app(beta) では、デフォルトで全てのコンポーネントが SC として扱われ Vercel 等にサーバーとしてホスティングすることで従来どおり SSR を行ってくれる。
コードは以下で、yarn create next-app --experimental-app
したものをそのまま利用。
https://github.com/pokuwagata/rsc-ssr-example
Vercel にデプロイした。
https://rsc-ssr-example.vercel.app/
<script> self.__next_f.push([ 1, 'J1:["$","@2",null,{"assetPrefix":"","initialCanonicalUrl":"/","initialTree":["",{"children":["",{}]},null,null,true],"initialHead":[["$","title",null,{"children":"Create Next App"}],["$","meta",null,{"content":"width=device-width, initial-scale=1","name":"viewport"}],["$","meta",null,{"name":"description","content":"Generated by create next app"}],["$","link",null,{"rel":"icon","href":"/favicon.ico"}]],"globalErrorComponent":"$3","children":[null,null,[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/876d048b5dab7c28.css","precedence":"high"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{}],["$","body",null,{"children":["$","@4",null,{"parallelRouterKey":"children","segmentPath":["children"],"hasLoading":false,"template":["$","@5",null,{}],"notFound":["$","div",null,{"style":{"fontFamily":"-apple-system, ...
HTML を確認すると、このような RSC プロトコルが埋め込まれていて、ここから React コンポーネントツリーを生成して hydrateRoot
に渡す模様。
Chrome の Performance タブから Eventlog を見ると以下のように Hydration が実行されている。
通常の Next.js 13 の Hydration
コードは以下で、yarn create next-app
しただけ。
https://github.com/pokuwagata/no-rsc-ssr
Vercel にデプロイした。
https://no-rsc-ssr.vercel.app/
<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/","query":{},"buildId":"lJbXQiPUmF0U5zQNgNPB2","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script>
HTML を確認すると、このように通常の Hydration 用のデータが埋め込まれているだけなので、Hydration には HTML 文字列をそのまま利用すると思われる(RSC 以前の 典型的 Hydration)
Chrome の Performance タブから Eventlog を見ると以下のように Hydration が実行されている。
RSC の場合に比べると、Hydration 時間が長い。React が解釈しやすいように予め生成されているプロトコルを利用する方が Hydration しやすいということなのかもしれない。
パフォーマンス
Lighthouse で測定して比較してみると、若干 RSC を使っている方が TTI や TBT が小さく出る傾向がありそうだった。
RSC を使った場合
RSC を使わない場合