プリレンダリング



プリレンダリングを使用すると、ページをランタイムではなくビルド時にレンダリングすることで、静的コンテンツのページロードを高速化できます。

設定

プリレンダリングは、react-router.config.tsprerender 設定によって有効になります。

最もシンプルな設定はブーリアンの true で、これは routes.ts に基づいてアプリケーションのすべての静的パスをプリレンダリングします。

react-router.config.ts
import type { Config } from "@react-router/dev/config";
 
export default {
  prerender: true,
} satisfies Config;

ブーリアンの true は、パラメータ値が不明なため、動的パス (例: /blog/:slug) は含まれません。

動的な値を含む特定のパスを設定するには、パスの配列を指定できます。

react-router.config.ts
import type { Config } from "@react-router/dev/config";
 
let slugs = getPostSlugs();
 
export default {
  prerender: [
    "/",
    "/blog",
    ...slugs.map((s) => `/blog/${s}`),
  ],
} satisfies Config;

パスを決定するためにより複雑な、または非同期のロジックを実行する必要がある場合は、パスの配列を返す関数を提供することもできます。この関数は、アプリケーション内のすべての静的パスを手動で追加することを避けるために使用できる getStaticPaths メソッドを提供します。

react-router.config.ts
import type { Config } from "@react-router/dev/config";
 
export default {
  async prerender({ getStaticPaths }) {
    let slugs = await getPostSlugsFromCMS();
    return [
      ...getStaticPaths(), // "/" and "/blog"
      ...slugs.map((s) => `/blog/${s}`),
    ];
  },
} satisfies Config;

Concurrency (unstable)

この API は実験的であり、マイナー/パッチリリースで破壊的変更の対象となる可能性があります。注意して使用し、関連する変更についてはリリースノートに非常に注意してください。

デフォルトでは、ページは一度に1つのパスでプリレンダリングされます。並行して複数のパスをプリレンダリングするように concurrency を有効にすることで、多くの場合、ビルド時間を短縮できます。アプリに最適なパフォーマンスを提供する値で実験する必要があります。

concurrency を指定するには、prerender 設定を prerender.paths フィールドに移動し、prerender.unstable_concurrency で concurrency を指定できます。

react-router.config.ts
import type { Config } from "@react-router/dev/config";
 
let slugs = getPostSlugs();
 
export default {
  prerender: {
    paths: [
      "/",
      "/blog",
      ...slugs.map((s) => `/blog/${s}`),
    ],
    unstable_concurrency: 4,
  },
} satisfies Config;

ランタイムサーバーの有無によるプリレンダリング

プリレンダリングは、ssr 設定値に基づいて次の2つの方法で使用できます。

  • ssr:true (デフォルト値) を使用したランタイム SSR サーバーと連携
  • ssr:false を使用して静的ファイルサーバーにデプロイ

ssr:true を使用したプリレンダリング

ssr:true を使用してプリレンダリングする場合、ランタイムサーバーは引き続き存在しますが、応答時間を短縮するために特定のパスをプリレンダリングすることを選択していることを示します。

react-router.config.ts
import type { Config } from "@react-router/dev/config";
 
export default {
  // 省略可能 - デフォルトは true
  ssr: true,
  prerender: ["/", "/blog", "/blog/popular-post"],
} satisfies Config;

データローディングとプリレンダリング

プリレンダリングのための特別なアプリケーション API はありません。プリレンダリングされるルートは、サーバーレンダリングと同じルート loader 関数を使用します。

export async function loader({ request, params }) {
  let post = await getPost(params.slug);
  return post;
}
 
export function Post({ loaderData }) {
  return <div>{loaderData.title}</div>;
}

デプロイされたサーバー上のルートへのリクエストの代わりに、ビルドは new Request() を作成し、サーバーと同じようにアプリを通して実行します。

サーバーレンダリングの場合、プリレンダリングされていないパスへのリクエストは、通常どおりサーバーレンダリングされます。

静的ファイル出力

レンダリングされた結果は、build/client ディレクトリに書き出されます。各パスに対して 2 つのファイルが表示されます。

  • 初期ドキュメントリクエスト用の [url].html HTML ファイル
  • クライアント側のナビゲーションブラウザリクエスト用の [url].data ファイル

ビルドの出力には、プリレンダリングされたファイルが示されます。

> react-router build
vite v5.2.11 building for production...
...
vite v5.2.11 building SSR bundle for production...
...
Prerender: Generated build/client/index.html
Prerender: Generated build/client/blog.data
Prerender: Generated build/client/blog/index.html
Prerender: Generated build/client/blog/my-first-post.data
Prerender: Generated build/client/blog/my-first-post/index.html
...

開発中、プリレンダリングはレンダリングされた結果をパブリックディレクトリに保存しません。これは react-router build でのみ発生します。

ssr:false を使用したプリレンダリング

上記の例では、ランタイムサーバーをデプロイしているが、一部の静的ページをプリレンダリングしてサーバーへのアクセスを回避し、ロードを高速化することを前提としています。

ランタイム SSR を無効にし、静的ファイルサーバーから提供されるようにプリレンダリングを構成するには、ssr:false 構成フラグを設定します。

react-router.config.ts
import type { Config } from "@react-router/dev/config";
 
export default {
  ssr: false, // ランタイムサーバーレンダリングを無効にする
  prerender: true, // すべての静的ルートをプリレンダリングする
} satisfies Config;

prerender 構成なしで ssr:false を指定すると、React Router はそれを SPA モード と呼びます。SPA モードでは、アプリケーションパスの いずれか をハイドレートできる単一の HTML ファイルをレンダリングします。これは、root ルートのみを HTML ファイルにレンダリングし、ハイドレーション中にブラウザの URL に基づいてロードする子ルートを決定するためです。つまり、ルートルートで loader を使用できますが、他のルートでは使用できません。これは、ブラウザでのハイドレーションまでどのルートをロードするかわからないためです。

ssr:false でパスをプリレンダリングする場合、それらのパスに一致するすべてのルートをプリレンダリングするため、一致するルートはローダーを持つ_ことができます_。ssr:false が設定されている場合、actions または headers 関数をルートに含めることはできません。これは、それらを実行するランタイムサーバーがないためです。

SPA フォールバックを使用したプリレンダリング

ssr:false が必要だが、ルートの すべて をプリレンダリングしたくない場合も問題ありません。プリレンダリングのパフォーマンス/SEO の利点が必要なパスもあれば、SPA で問題ないページもあるかもしれません。

構成オプションの組み合わせを使用してこれを行うこともできます。prerender 構成をプリレンダリングするパスに制限するだけで、React Router は他のパスをハイドレートするために提供できる「SPA フォールバック」HTML ファイルも出力します (SPA モード と同じアプローチを使用)。

これは、次のいずれかのパスに書き込まれます。

  • build/client/index.html - / パスがプリレンダリングされていない場合
  • build/client/__spa-fallback.html - / パスがプリレンダリングされている場合
react-router.config.ts
import type { Config } from "@react-router/dev/config";
 
export default {
  ssr: false,
 
  // SPA フォールバックは build/client/index.html に書き込まれます
  prerender: ["/about-us"],
 
  // SPA フォールバックは build/client/__spa-fallback.html に書き込まれます
  prerender: ["/", "/about-us"],
} satisfies Config;

デプロイサーバーを構成して、それ以外の場合は 404 になるパスに対してこのファイルを提供できます。一部のホストはデフォルトでこれを行いますが、そうでないホストもあります。例として、ホストはこれを実行するために _redirects ファイルをサポートする場合があります。

# `/` ルートをプリレンダリングしなかった場合
/*    /index.html   200

# `/` ルートをプリレンダリングした場合
/*    /__spa-fallback.html   200

アプリの有効なルートで 404 が発生する場合は、ホストを構成する必要がある可能性があります。

sirv-cli ツールでこれを行う方法の別の例を次に示します。

# `/` ルートをプリレンダリングしなかった場合
sirv-cli build/client --single index.html
 
# `/` ルートをプリレンダリングした場合
sirv-cli build/client --single __spa-fallback.html

無効なエクスポート

ssr:false でプリレンダリングする場合、React Router はビルド時に無効なエクスポートがある場合にエラーを発生させ、見落としやすい一部の間違いを防ぎます。

  • headers/action 関数は、それらを実行するランタイムサーバーがないため、すべてのルートで禁止されています
  • prerender 構成なしで ssr:false (SPA モード) を使用する場合、loader はルートルートでのみ許可されます
  • prerender 構成で ssr:false を使用する場合、loaderprerender パスによって一致するすべてのルートで許可されます
    • 子ルートを持つプリレンダリングされたルートで loader を使用している場合は、次のいずれかの方法で、実行時に親 loaderData を適切に決定できることを確認する必要があります。
      • すべての子ルートをプリレンダリングして、各子ルートパスのビルド時に親 loader を呼び出して .data ファイルにレンダリングできるようにするか、
      • プリレンダリングされていない子パスに対して実行時に呼び出すことができる親で clientLoader を使用します