Open12

React Server Component

りんだりんだ
  • サーバー側でJavaScript(async, await)でdata fetchしたのち、Reactを完全にただのHTMLに変換する。その後クライアント側には、JS抜きでHTMLとCSSしか運ばないので、通信量が軽くなり、運ぶ時間が短くなる
  • クライアント側にはJavaScriptは運ばれないので、クライアント全体のJSバンドルサイズが減り、それによりJSバンドルの時間が減る
  • クライアント全体のJSのバンドルサイズが減るため、ハイドレーションの時間が減る
    • ハイドレーションはJSをHTMLに染み込ませることなので、サバコン自体にハイドレーションはないが、クラコン分がある。

上3つの観点で、リクエストを送ってからページが表示されるまでの時間が短縮される。

https://zenn.dev/sumiren/articles/f39a151e7320d5
https://zenn.dev/tm35/articles/0a64177c0a41bd

りんだりんだ

RSCとSSRは違う

SSR: サーバー側でreact tree全体に対してHTMLに変換する処理を行い、クライアント側ではhydrationだけ行う形式。

RSC: react treeのうち、サーバー側でだけHTML変換するノード(コンポーネント)を指定することができ、指定しなかったノードはクライアント側でHTML変換(ハイドレーションか?)される。data fetchなどサーバー側のrenderだとより都合の良いノードを選び、パフォーマンスを最適化する。

https://postd.cc/how-react-server-components-work/#02

https://jp.quora.com/SSRで返すhtmlと従来のphpなどで返すhtmlファイルの違い-または/answers/321311204

りんだりんだ

クラコンにサバコンをimportはできない

Server Componentはサーバー時点でReactからHTMLに変換される。そのため、クライアントに運ばれる際の、関数コンポーネント分のバンドルサイズが減る。

サバコンはサーバー側でReactからHTMLに完全に変わったあとにクライアントに運ばれるから、クラコンで、サバコンをimportして使うことができない。なぜなら、コードに記載してるのはReact状態のサバコンだけど、実際にクラコンがimportするときのサバコンはただのHTMLとなっていて、React状態ではなく差異があるから。
しかしchildrenを使うと実現できる。

https://zenn.dev/uhyo/articles/react-server-components-multi-stage

りんだりんだ

SSRとRSCのHTML変換とAppRouter

SSRではサーバー側で完成形のHTMLを生成してクライアントに返すが、RSCでは仮想DOMを返す。 RSCも、サバコンはHTML化されて返される。

App Routerキャッシュを無効化したり、動的関数cookies()、headers()を使用したRSCは、自動的に動的レンダリング(SSR)になる。

https://zenn.dev/tfutada/articles/36ad71ab598019

りんだりんだ

Q. サバコンからクラコンをimportしてもエラーにならないのはなぜか

A. React Tree的には、サーバー時点ではクラコンにプレースホルダー(空のnode)が入るため。

サーバー側で、サバコンはReact to HTMLされた後、文字列にシリアライズされ、まだ中身のないクラコンも文字列にシリアライズされる。

クラコンの文字列はこんな感じのjsonで、モジュール参照オブジェクトと呼ばれる。

このjsonが空のnodeとして機能するので、サーバー時点でサバコンはクラコンを正常にimportできている(中身はまだないが)。

以下は、サーバーでReact to HTMLをしたあとのReact Treeを可視化した図。

りんだりんだ

RSCとSuspense、Streaming

サバコンでSuspenseを使うと、サーバー側で、data fetchができてないなどローディング状態のときに、コンポーネントを、上記のモジュール参照オブジェクトとして出力する。

そこから準備が出来次第、Reactコンポーネントを作り、それをHTMLに変換してクライアントに送る。

https://postd.cc/how-react-server-components-work/#02

Streamingとは、HTMLを小さなチャンクに分解し、その塊チャンクをサーバーからクライアントに順次送信する仕組みのこと。
もともとページ全体でしか送信ができなかったSSR用の概念だったようだが、それが今はRSCにも適用されている(ように見える)。

https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense

RSCもStreamingもパフォチューの概念であり、そのため今はそれらを併用した以下のような形でやってくのがベターだと思われる。