progressive-hydration について
React SSR 開発者向けに、ページのコンポーネントを段階的にハイドレーションする方法を解説します。初期読み込み時の JavaScript を減らすため、重要度の低い UI を後回しにします。
主な機能
- 01重要度の低いコンポーネントのハイドレーション延期
- 02ページコンポーネントのオンデマンド読み込み
- 03コード分割によるスクリプト削減
- 04表示状態や条件に応じたハイドレーション
活用シーン
- 01SSR ページの重要領域を先に操作可能にする
- 02画面外や使用頻度の低いコンポーネントを後回しにする
- 03初期読み込み時に必要な JavaScript を減らす
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill progressive-hydrationインストール後、Claude Code で/progressive-hydrationを入力して読み込まれたか確認します。
詳細
プログレッシブハイドレーション
サーバーレンダリングを使用するアプリケーションでは、サーバーが現在のナビゲーション用の HTML を生成します。サーバーが HTML コンテンツの生成を完了すると、静的 UI を正しく表示するために必要な CSS と JSON データも含めて、クライアントにデータを送信します。サーバーがマークアップを生成してくれるため、クライアントはそれをすばやく解析して画面に表示でき、First Contentful Paint を高速化できます!
サーバーレンダリングによって First Contentful Paint は高速になりますが、Time To Interactive が常に短縮されるわけではありません。Web サイトを操作できるようにするために必要な JavaScript が、まだ読み込まれていないためです。ボタンは操作できそうに見える 場合があります が、まだ操作できません。ハンドラーは、JavaScript バンドルが読み込まれて処理された後にのみアタッチされます。このプロセスを ハイドレーション と呼びます。React は現在の DOM ノードを確認し、対応する JavaScript をノードにハイドレートします。
使用する場面
- SSR アプリケーションに、すぐに操作可能にする必要のない重要度の低いセクションがある場合に使用します
- 初回読み込み時にページを操作可能にするために必要な JavaScript を減らすのに役立ちます
使用しない場面
- ページ全体が操作可能で、すべてのコンポーネントをすぐにハイドレートする必要がある場合
- ハイドレーション境界の管理が複雑になり、パフォーマンス上のメリットを上回る場合
- ページが小さく、JavaScript の総量がすでに最小限で、ハイドレーションも高速な場合
手順
- 適切なフォールバックを指定して、重要度の低いコンポーネントを
<Suspense>境界で囲みます React.lazy()とコード分割を使用し、ページ下部にあるコンポーネントや使用頻度の低いコンポーネントの読み込みを遅らせます- React 18 以降では、ユーザーが操作した領域を自動的に優先する選択的ハイドレーションを活用します
- Next.js では、真にクライアント専用のウィジェットに
{ ssr: false }を指定してnext/dynamicを使用します
詳細
画面上に操作できない UI が表示される時間は、アンキャニーバレーとも呼ばれます。ユーザーには Web サイトを操作できるように見えても、コンポーネントにはまだハンドラーがアタッチされていません。UI がフリーズしているように見えることもあるため、ユーザーにとってストレスのたまる体験になり得ます。
サーバーから受信した DOM コンポーネントが完全にハイドレートされるまでには、時間がかかる場合があります。コンポーネントをハイドレートする前に、JavaScript ファイルを読み込み、処理し、実行する必要があります。以前のようにアプリケーション全体を一度にハイドレートするのではなく、DOM ノードを 段階的にハイドレート することもできます。プログレッシブハイドレーションでは、ノードを個別に時間をかけてハイドレートできるため、必要最小限の JavaScript だけをリクエストできます。
アプリケーションを段階的にハイドレートすることで、ページの重要度が低い部分のハイドレーションを遅らせることができます。これにより、ページを操作可能にするためにリクエストする JavaScript の量を減らし、ユーザーが必要とした時点でのみノードをハイドレートできます。また、プログレッシブハイドレーションは、サーバーレンダリングされた DOM ツリーが破棄された後、直ちに再構築されるという、SSR のよくある再ハイドレーションの落とし穴を避けるのにも役立ちます。
プログレッシブハイドレーションでは、たとえばコンポーネントがビューポート内に表示されたときなど、特定の条件に基づいてコンポーネントのみをハイドレートできます。
プログレッシブハイドレーションの実装
React で SSR を実装するセクションでは、サーバーでレンダリングされるアプリのクライアント側ハイドレーションについて説明しました。ハイドレーションによって、クライアント側の React はサーバーでレンダリングされた ReactDOM コンポーネントを認識し、それらのコンポーネントにイベントをアタッチできます。これにより、SSR アプリはクライアントで利用可能になると、CSR アプリのように動作し、継続性とシームレスさを実現します。 ページ上のすべてのコンポーネントをハイドレーションによってインタラクティブにするには、これらのコンポーネントの React コードを、クライアントにダウンロードされるバンドルに含める必要があります。JavaScript によって大部分が制御される、インタラクティブ性の高い SPA では、バンドル全体を一度に読み込む必要があります。しかし、ほとんどが静的で、画面上にインタラクティブな要素がいくつかある程度のウェブサイトでは、すべてのコンポーネントをすぐに有効にする必要はないかもしれません。このようなウェブサイトで、画面上の各コンポーネント用に巨大な React バンドルを送信するのは、オーバーヘッドになります。
プログレッシブハイドレーションを使うと、ページの読み込み時にアプリケーションの一部だけをハイドレーションできるため、この問題を解決できます。ほかの部分は、必要に応じて段階的にハイドレーションされます。
アプリケーション全体を一度に初期化するのではなく、ハイドレーションの処理は DOM ツリーのルートから始まりますが、サーバーでレンダリングされたアプリケーションの個々の部分は、時間をかけて有効化されます。ハイドレーションの処理は、さまざまなブランチで中断し、ブランチがビューポートに入ったときや、ほかのトリガーに基づいて後から再開できます。各ハイドレーションに必要なリソースの読み込みも、コード分割の手法によって遅延されるため、ページをインタラクティブにするために必要な JavaScript の量を減らせます。
プログレッシブハイドレーションの目的は、アプリをまとまりごとに有効化し、優れたパフォーマンスを実現することです。プログレッシブハイドレーションのソリューションでは、全体的なユーザーエクスペリエンスへの影響も考慮する必要があります。包括的なプログレッシブハイドレーションを実装するには、次の要件を満たす必要があります。
- すべてのコンポーネントで SSR を使用できる。
- コードを個々のコンポーネントまたはチャンクに分割できる。
- 開発者が定義した順序で、これらのチャンクをクライアント側でハイドレーションできる。
- すでにハイドレーション済みのチャンクで、ユーザー入力がブロックされない。
- ハイドレーションが遅延されるチャンクに、何らかのローディングインジケーターを使用できる。
React の並行モードは、すべてのユーザーが利用できるようになれば、これらの要件をすべて満たします。React は複数のタスクを同時に処理し、指定された優先度に基づいてタスクを切り替えられます。切り替え時に、部分的にレンダリングされたツリーをコミットする必要はないため、React が同じタスクに戻ると、レンダリングタスクを続行できます。
並行モードを使ってプログレッシブハイドレーションを実装できます。この場合、ページ上の各チャンクのハイドレーションが、React 並行モードのタスクになります。ユーザー入力のような優先度の高いタスクを実行する必要がある場合、React はハイドレーションタスクを一時停止し、ユーザー入力の受け付けに切り替えます。lazy() や Suspense() などの機能を使うと、宣言的なローディング状態を利用できます。これらを使って、チャンクが遅延読み込みされている間にローディングインジケーターを表示できます。SuspenseList() を使うと、コンポーネントの遅延読み込みの優先度を定義できます。
React の並行モードは、次の React の機能と組み合わせることもできます。
- サーバーコンポーネント。これにより、取得全体が完了するのを待つのではなく、サーバーからコンポーネントを再取得し、ストリーミングに応じてクライアントでレンダリングできます。これにより、ネットワークからの取得完了を待っている間も、クライアントの CPU を活用できます。
プログレッシブハイドレーションには、複数の実装方法があります。Preact と Next.js を使った部分ハイドレーションの POC では、次のものを使用します。
pool-attendant-preact:Preact x で部分ハイドレーションを実装するライブラリです。next-super-performance:このライブラリを使ってクライアント側のパフォーマンスを向上させる Next.js プラグインです。
pool-attendant-preact ライブラリには、インタラクティブ性の高いコンポーネントにハイドレーションの対象としてマークを付けられる withHydration という API が含まれています。これらのコンポーネントが最初にハイドレーションされます。
import Teaser from "./teaser";
import { withHydration } from "next-super-performance";
const HydratedTeaser = withHydration(Teaser);
export default function Body() {
return (
<main>
<Teaser column={1} />
<HydratedTeaser column={2} />
<HydratedTeaser column={3} />
<Teaser column={1} />
<Teaser column={2} />
<Teaser column={3} />
<Teaser column={1} />
<Teaser column={2} />
<Teaser column={3} />
</main>
);
}
2 列目と 3 列目の HydratedTeaser コンポーネントが最初にハイドレーションされます。残りのコンポーネントは、hydrate() API を使ってクライアント側でハイドレーションできます。
import { hydrate } from "next-super-performance";
import Teaser from "./components/teaser";
hydrate([Teaser]);
メリットとデメリット
プログレッシブハイドレーションでは、サーバーサイドレンダリングとクライアントサイドハイドレーションを実現しながら、ハイドレーションにかかるコストも最小限に抑えられます。
-
コード分割を促進します:プログレッシブハイドレーションでは、遅延読み込みされる個々のコンポーネント用にコードのチャンクを作成する必要があるため、コード分割が欠かせません。
-
使用頻度の低いページの部分を必要に応じて読み込めます:ページには、ほぼ静的で、ビューポートの外にあり、かつ使用頻度が低いコンポーネントが含まれる場合があります。このようなコンポーネントは、遅延読み込みに最適です。
-
バンドルサイズを縮小します:コード分割により、バンドルサイズが自動的に縮小されます。読み込み時に実行するコードが少なくなることで、FCP から TTI までの時間を短縮できます。
一方、画面上のすべての要素をユーザーが利用でき、読み込み時にインタラクティブにする必要がある動的なアプリには、プログレッシブハイドレーションが適さない場合があります。これは、ユーザーが最初にどこをクリックするかを開発者が把握できない場合、どのコンポーネントを最初にハイドレーションすべきか特定できない可能性があるためです。