Next.js ver.13 のSSR / SSG(ISR)がどのように変わったのか

React Server Componentがデフォルトになったらしい。 それは、これまでのNext.jsのSSR / SSGにどのように影響したのか? React Server Component を理解できていなかったので調べてみた。

Next.jsのこれまでのSSR

まず、これまでのNext.jsのServer Side Renderingについて振り返る。

SSRする場合は、getServerSideProps をページコンポーネントの中で定義。 SSGの場合は、getStaticProps or getStaticPaths を定義する必要があった。

pageコンポーネントの中で定義されているそれらの関数を判別して、それぞれのページがSSRするのか、SSGするのかの判定を行なっていた。

React Server Component

Next.js ver.13から、React Server Componentが defaultとなった。 あくまでReact Server Componentがデフォルトとなっただけで、React Server Componentがページ自体のSSRを表すものではない。その名の通り、コンポーネント単位でSSRができるようになっている。

では、React Server ComponentがデフォルトになったことでUI/UXにどのような影響を与えるか?

React Server Componentは、コンポーネント -> ReactElement への変換を行うのみで、厳密にレンダリングはクライアント側で行なっている。

こうすることで、コンポーネントのレンダリングに必要な重い処理やライブラリをサーバ側で予めレンダリングしておき、クライアントからは描画結果を取得してくるのみで済む。

クライアント側に送るJavaScriptのコード量を減らすことができる。

また、あくまでReactElementを返すので、clientコンポーネントをserverコンポーネントに含むことができる。(逆はできない)

それだけでなく、Reactコンポーネントにisormorphismが生まれる。 どういうことかというと、これまでのNext.jsはgetServerSideProps の内部でデータ取得を行ない、取得結果をコンポーネントにPropsとして渡す必要があった。これに対してReact Server ComponentではHooksを使用するなど、clientコンポーネントと同様の書き味でデータフェッチを表現できるようになった。

async function getPosts(): Promise<Post[]> {
  const posts = await getAllPosts(['slug', 'content', 'title', 'date'])
  console.log('getPosts', posts)
  return posts
}

export default async function Home() {
  const recentPosts = await getPosts()
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <PostList posts={recentPosts} />
      </main>
    </div>
  )
}

動的レンダリングと静的レンダリング

Next.jsのドキュメントに記載があるので詳しくはそちらを参照。
Rendering説明
動的レンダリングリクエストオンデマンドにレンダリングを行う。SSR。
静的レンダリングNext.jsサーバのビルドタイミングにてレンダリングを行う。SSG。

ver13からは、以下の特徴となった。

  • デフォルトで静的レンダリング
  • 動的関数cookies(), headers())がページコンポーネントにて呼び出されている場合のみ、それを検知して動的レンダリング(SSR)となる。

React Server Componentの振る舞いの違い

では、React Server ComponentはSSR/SSGの場合振る舞いにどう違いが生じるのか?これも公式ドキュメントに記載があります。

(TODO: 要検証) SSRの場合、React Server Componentの恩恵を特に感じられる(レンダリング時に返されるJavaScriptコードが減る?)

SSR、SSGいずれもReactElementを返す。SSRはキャッシュが効かず、常に再取得を行う。SSGはキャッシュからデータ(出来上がったReactElement)を返すのみとなる。

UI/UX面でのメリット

SSR/SSGはこれまでの挙動からUI/UX面でどういった変化があるか?が不明瞭。 TODO: 要検証 (挙動自体はそんなに変化はないのではないか?これまでも、React Server Component と同様のことはSSRで行なっていたはず。)

では、何のためのReact Server Component? 以下推測。

  • コンポーネントのIsormorphism
  • Server 実行とclient実行の明確な区分けがなかった。これまでは、ページコンポーネントでnot-universalなコードを書くとランタイムエラーで怒られるなどしており、それがユーザフレンドリーな設計じゃなかった?
  • 今後Reactの仕様となるであろう機能を、VercelがNext.jsによい形で組み込んだ

と、どちらかというとフレームワークの書き味的な部分が大きいのかもしれないと思った。