Streaming with Suspense(SSRの限界)
でも、SSRは順番に処理するから、パフォーマンス的にちょっとキツい部分がある。
その問題を解決するために Streaming が導入されて、さらに Suspense を使うともっと効率よくなる。
SSRの限界と、それを Streaming でどうやって解決できるのかを解説しようと思う。
SSRのプロセスと限界
SSRは「Dynamic Rendering」とも呼ばれ、クライアントがリクエストするたびに新しいHTMLを生成する方式である。
基本的なSSRのプロセスは以下の通り。
- データフェッチ(Fetching): サーバーで必要なデータを取得する。
- HTMLレンダリング: データを含んだHTMLをサーバーで生成する。
- クライアントローディング: ブラウザがHTML、CSS、JavaScriptをダウンロードする。
- Hydration: Reactがアクティブになり、インタラクションが可能になる。
しかし、SSRは順次(Sequential)で実行されるため、いくつかの制約がある。
例えば、ページに「おすすめ商品セクション」と「フッター」があるとしよう。おすすめ商品はサーバーからデータを取得する必要があるが、フッターは静的なコンテンツなので即座にレンダリングできる。しかし、SSRでは処理が順番に進行するため、おすすめ商品のデータをすべて取得するまでフッターがレンダリングされないという問題が発生する。
Streamingを利用した解決策
Streamingを使用すると、ページ内の特定のコンポーネント(例:静的なレイアウトや即座にレンダリング可能なUI)と、データを取得する必要があるコンポーネント(例:おすすめ商品)を分離し、並列で処理することができる。
Streaming方式の特徴
- データを必要としない静的なコンポーネントは先にレンダリングされる。
- データを必要とするコンポーネントは別途ロードされ、完了次第画面に反映される。
- これを実現するために、ReactのSuspenseを活用する。
import { Suspense } from 'react';
import FechingDataComponent from './FechingDataComponent';
import StaticComponent from './StaticComponent';
export default function Page() {
return (
<div>
<StaticComponent /> {/* 静的なUIは即座にレンダリング */}
<Suspense fallback={<div>Loading...</div>}>
<FetchingDataComponent /> {/* データが準備でき次第レンダリング */}
</Suspense>
</div>
);
}
上記のようにSuspenseを使用すると、StaticComponent は即座に画面に表示され、FetchingDataComponent はデータが準備できた後にレンダリングされる。
Streaming vs. PPR (Partial Prerendering)
Next.jsでは Partial Prerendering(PPR) という概念も登場した。PPRはStreamingと類似しているが、主な違いは以下の通りである。
- Streaming: ページをサーバーで順次レンダリングするのではなく、特定のコンポーネント単位でデータを非同期ロードする。
- PPR: 特定の動的コンポーネントを事前に静的レンダリング(SSG)した後、クライアント側で動的にデータを結合する。
つまり、Streaming はサーバーでデータを取得しながら並行してレンダリングを進めるのに対し、PPR は事前に準備されたHTMLを活用して、高速な初期ロードを提供する方式である。
※Next.js15のcanaryバージョンでも実験的機能として提供されているので、まだ安定的ではない
Streaming | PPR (Partial Prerendering) | |
---|---|---|
概念 | サーバーからデータを取得しながら順次ページをレンダリングし、ストリーミングする | あらかじめ静的レンダリング(SSG)されたHTMLを活用し、一部のコンポーネントはクライアントで動的に結合 |
初期ローディング速度 | 初期HTMLをすぐに送るが、一部のデータはロードされながら段階的に表示される | 初期HTMLが事前に準備されているため、はるかに速い初回ローディングを提供 |
サーバーの役割 | リクエストを受けるたびにサーバーがデータを取得し、必要な部分をストリーミング | 一部の静的部分をあらかじめ生成し、残りはクライアントで動的に処理 |
クライアントの役割 | ブラウザがHTMLを受け取り、サーバーから追加データを取得しながら画面を段階的に完成 | 静的なHTMLを先に受け取って素早く表示し、残りのデータはクライアントで結合 |
SEO | SEO最適化は可能だが、すべてのデータが一度に来ないため、クローラーが完全に解析するのに時間がかかる可能性あり | 事前に生成されたHTMLがあるため、SEOに有利 |
使用例 | 大規模ページでリアルタイムにデータを取得する場合(例:ニュースフィード、無限スクロール) | 静的ページだが、一部のデータをユーザーごとにカスタマイズする必要がある場合(例:パーソナライズされたダッシュボード) |
Next.js対応バージョン | Next.js 13+ | Next.js 14+(試験的機能) |
結論
Next.jsで Streaming with Suspense を活用すると、SSRの順次処理による問題を解決できる。静的なUIを先にレンダリングし、データが必要なコンポーネントは Suspense を利用して非同期に読み込むことで、ユーザーエクスペリエンス(UX)とパフォーマンスを向上 させることが可能である。
また、PPRとの比較では、それぞれに長所と短所があるため、プロジェクトの要件に応じて適切な方法を選択することが重要 である。
Discussion