Menu

Next.jsでのキャッシング

Next.jsはレンダリング処理とデータリクエストをキャッシュすることで、アプリケーションのパフォーマンスを向上させ、コストを削減します。このページでは、Next.jsのキャッシング機構、それらを設定するために使用できるAPI、およびそれらの相互作用について詳しく説明します。

補足:このページはNext.jsがどのように機能するかを理解するのに役立ちますが、Next.jsを効果的に使用するために必須ではありません。Next.jsのキャッシング戦略のほとんどはAPI使用法によって決定され、ゼロまたは最小限の設定で最高のパフォーマンスを実現するようにデフォルト設定されています。すぐに例を見たい場合は、こちらから開始してください

概要

ここでは、異なるキャッシング機構とその目的の高度な概要を示します。

機構内容場所目的期間
リクエストメモ化関数の戻り値サーバーReactコンポーネントツリー内でデータを再利用リクエストライフサイクルごと
データキャッシュデータサーバーユーザーリクエストとデプロイメント間でデータを保存永続的(再検証可能)
完全ルートキャッシュHTMLとRSCペイロードサーバーレンダリングコストを削減してパフォーマンスを向上永続的(再検証可能)
ルーターキャッシュRSCペイロードクライアントナビゲーション時のサーバーリクエストを削減ユーザーセッションまたは時間ベース

デフォルトでは、Next.jsはパフォーマンスを向上させ、コストを削減するためにできるだけ多くをキャッシュします。つまり、ルートは静的にレンダリングされ、データリクエストは、オプトアウトしない限りキャッシュされます。以下の図は、デフォルトのキャッシング動作を示しています。ビルド時に静的にレンダリングされるルートと、静的ルートが最初に訪問されるときです。

Next.jsの4つの機構のデフォルトキャッシング動作を示す図、ビルド時とルートが最初に訪問されるときのHIT、MISS、SET。

キャッシング動作は、ルートが静的または動的にレンダリングされるかどうか、データがキャッシュまたはキャッシュされていないかどうか、およびリクエストが初回訪問または後続のナビゲーションの一部であるかどうかによって変わります。ユースケースに応じて、個別のルートとデータリクエストのキャッシング動作を設定できます。

フェッチキャッシングはproxy内ではサポートされていません。proxy内で実行されるフェッチはすべてキャッシュされません。

レンダリング戦略

Next.jsでキャッシングがどのように機能するかを理解するには、利用可能なレンダリング戦略を理解することが役立ちます。レンダリング戦略は、ルートのHTMLが生成されるタイミングを決定し、これはキャッシュできるものに直接影響します。

静的レンダリング

静的レンダリングでは、ルートはビルド時に、またはデータ再検証後にバックグラウンドでレンダリングされます。結果はキャッシュされ、リクエスト間で再利用できます。静的ルートは完全ルートキャッシュに完全にキャッシュされます。

動的レンダリング

動的レンダリングでは、ルートはリクエスト時にレンダリングされます。これは、ルートがクッキー、ヘッダー、検索パラメータなどのリクエスト固有の情報を使用する場合に発生します。

ルートは、これらのAPIのいずれかを使用する場合に動的になります。

動的ルートは完全ルートキャッシュにキャッシュされませんが、データリクエストに対してデータキャッシュを使用できます。

補足キャッシュコンポーネントを使用して、同じルート内で静的レンダリングと動的レンダリングを混在させることができます。

リクエストメモ化

Next.jsはfetch APIを拡張して、同じURLとオプションを持つリクエストを自動的にメモ化します。これは、Reactコンポーネントツリー内の複数の場所で同じデータのフェッチ関数を呼び出しながら、一度だけ実行できることを意味します。

重複除外されたフェッチリクエスト

たとえば、ルート全体で同じデータを使用する必要がある場合(例、レイアウト、ページ、および複数のコンポーネント内)、ツリーの上部でデータをフェッチしてコンポーネント間でプロップを転送する必要はありません。代わりに、必要なコンポーネント内でデータをフェッチでき、同じデータに対して複数のネットワークリクエストを行う場合のパフォーマンスへの影響について心配する必要はありません。

app/example.tsx
TypeScript
async function getItem() {
  // `fetch`関数は自動的にメモ化され、結果は
  // キャッシュされます
  const res = await fetch('https://.../item/1')
  return res.json()
}
 
// この関数は2回呼び出されますが、最初の1回だけ実行されます
const item = await getItem() // キャッシュ MISS
 
// 2回目の呼び出しはルート内のどこにでも配置できます
const item = await getItem() // キャッシュ HIT

リクエストメモ化の仕組み

Reactレンダリング中にフェッチメモ化がどのように機能するかを示す図。
  • ルートをレンダリングする間、特定のリクエストが最初に呼び出されるとき、その結果はメモリに存在せず、キャッシュMISSになります。
  • したがって、関数が実行され、データは外部ソースからフェッチされ、結果はメモリに格納されます。
  • 同じレンダリングパス内のリクエストの後続の関数呼び出しはキャッシュHITになり、関数を実行せずにメモリからデータが返されます。
  • ルートがレンダリングされ、レンダリングパスが完了すると、メモリは「リセット」され、すべてのリクエストメモ化エントリがクリアされます。

補足

  • リクエストメモ化はReactの機能であり、Next.jsの機能ではありません。他のキャッシング機構とどのように相互作用するかを示すため、ここに含まれています。
  • メモ化はfetchリクエスト内のGETメソッドにのみ適用されます。
  • メモ化はReactコンポーネントツリーにのみ適用されます。これは以下を意味します。
    • generateMetadatagenerateStaticParams、レイアウト、ページ、およびその他のサーバーコンポーネント内のfetchリクエストに適用されます。
    • Reactコンポーネントツリーの一部ではないため、ルートハンドラー内のfetchリクエストには適用されません。
  • fetchが適さない場合(例、一部のデータベースクライアント、CMSクライアント、またはGraphQLクライアント)、React cache関数を使用して関数をメモ化できます。

期間

キャッシュはサーバーリクエストのライフタイム、Reactコンポーネントツリーがレンダリング完了するまで保持されます。

再検証

メモ化はサーバーリクエスト間で共有されず、レンダリング中にのみ適用されるため、再検証する必要はありません。

オプトアウト

メモ化はfetchリクエスト内のGETメソッドにのみ適用されます。POSTDELETEなどの他のメソッドはメモ化されません。このデフォルト動作はReactの最適化であり、これをオプトアウトすることはお勧めしません。

個別のリクエストを管理するには、AbortControllersignalプロパティを使用できます。

app/example.js
const { signal } = new AbortController()
fetch(url, { signal })

データキャッシュ

Next.jsには、データフェッチの結果を永続化し、受信したサーバーリクエストデプロイメント間で保持する組み込みのデータキャッシュがあります。これは、Next.jsがネイティブfetch APIを拡張して、サーバー上の各リクエストが独自の永続的なキャッシング意味論を設定できるようにすることで可能になります。

補足:ブラウザでは、fetchcacheオプションは、リクエストがブラウザのHTTPキャッシュとどのように相互作用するかを示しますが、Next.jsでは、cacheオプションは、サーバー側のリクエストがサーバーのデータキャッシュとどのように相互作用するかを示します。

fetchcacheおよびnext.revalidateオプションを使用して、キャッシング動作を設定できます。

開発モードでは、fetchデータはホットモジュール置換(HMR)に対して再利用され、ハードリフレッシュに対してキャッシングオプションは無視されます。

データキャッシュの仕組み

キャッシュされたリクエストとキャッシュされていないフェッチリクエストがデータキャッシュとどのように相互作用するかを示す図。キャッシュされたリクエストはデータキャッシュに格納され、メモ化されます。キャッシュされていないリクエストはデータソースからフェッチされ、データキャッシュに格納されず、メモ化されます。
  • 'force-cache'オプション付きのfetchリクエストがレンダリング中に最初に呼び出されるとき、Next.jsはキャッシュされたレスポンスのデータキャッシュをチェックします。
  • キャッシュされたレスポンスが見つかった場合、それは即座に返され、メモ化されます。
  • キャッシュされたレスポンスが見つからない場合、リクエストはデータソースに対して行われ、結果はデータキャッシュに格納され、メモ化されます。
  • キャッシュされていないデータ(例、cacheオプションが定義されていない、または{ cache: 'no-store' }を使用している)の場合、結果は常にデータソースからフェッチされ、メモ化されます。
  • データがキャッシュされているかどうかに関わらず、リクエストは常にメモ化されて、Reactレンダリングパス中に同じデータの重複リクエストが行われるのを防ぎます。

データキャッシュとリクエストメモ化の違い

両方のキャッシング機構はキャッシュされたデータを再利用することでパフォーマンスの向上に役立ちますが、データキャッシュは受信したリクエストとデプロイメント間で永続的である一方、メモ化はリクエストのライフタイム中だけ保持されます。

期間

データキャッシュは、再検証またはオプトアウトしない限り、受信したリクエストとデプロイメント間で永続的です。

再検証

キャッシュされたデータは、2つの方法で再検証できます。

  • 時間ベースの再検証:一定の時間が経過して新しいリクエストが行われた後、データを再検証します。これは、変更されることがなく、新しさが重要でないデータに役立ちます。
  • オンデマンド再検証:イベント(例、フォーム送信)に基づいてデータを再検証します。オンデマンド再検証は、タグベースまたはパスベースのアプローチを使用して、一度に複数のデータグループを再検証できます。これは、最新のデータができるだけ早く表示されることを確認したいときに役立ちます(例、ヘッドレスCMSからのコンテンツが更新されるとき)。

時間ベースの再検証

タイミング間隔でデータを再検証するには、fetchnext.revalidateオプションを使用して、リソースのキャッシュライフタイムを設定できます(秒単位)。

// 最大1時間ごとに再検証
fetch('https://...', { next: { revalidate: 3600 } })

または、ルートセグメント設定オプションを使用して、セグメント内のすべてのfetchリクエストを設定するか、fetchを使用できない場合を設定できます。

時間ベースの再検証の仕組み

時間ベースの再検証がどのように機能するかを示す図。再検証期間後、古いデータは最初のリクエストに対して返され、その後データが再検証されます。
  • revalidate付きのフェッチリクエストが最初に呼び出されるとき、データは外部データソースからフェッチされ、データキャッシュに格納されます。
  • 指定された時間枠内に呼び出されるリクエスト(例、60秒)は、キャッシュされたデータを返します。
  • 時間枠後、次のリクエストは引き続き、キャッシュされた(現在古い)データを返します。
    • Next.jsはバックグラウンドでデータの再検証をトリガーします。
    • データが正常にフェッチされると、Next.jsはデータキャッシュを新しいデータで更新します。
    • バックグラウンド再検証が失敗した場合、前のデータは変更されないまま保持されます。

これはstale-while-revalidate動作に似ています。

オンデマンド再検証

データは、パスによるオンデマンドrevalidatePath、またはキャッシュタグによるrevalidateTagで再検証できます。

オンデマンド再検証の仕組み

オンデマンド再検証がどのように機能するかを示す図。再検証リクエスト後、データキャッシュが新しいデータで更新されます。
  • fetchリクエストが最初に呼び出されるとき、データは外部データソースからフェッチされ、データキャッシュに格納されます。
  • オンデマンド再検証がトリガーされると、適切なキャッシュエントリはキャッシュから削除されます。
    • これは時間ベースの再検証とは異なり、新しいデータがフェッチされるまで古いデータをキャッシュに保持します。
  • 次のリクエストが行われるとき、それはキャッシュMISSになり、データは外部データソースからフェッチされ、データキャッシュに格納されます。

オプトアウト

fetchからのレスポンスをキャッシュしたくない場合は、以下を実行できます。

let data = await fetch('https://api.vercel.app/blog', { cache: 'no-store' })

完全ルートキャッシュ

関連用語

自動静的最適化静的サイト生成、または静的レンダリングという用語が、ビルド時にアプリケーションのルートをレンダリング・キャッシュするプロセスを指すために互いに入れ替わりに使用されているのを見かけるかもしれません。

Next.jsは、ビルド時にルートを自動的にレンダリング・キャッシュします。これは、リクエストのたびにサーバーでレンダリングする代わりに、キャッシュされたルートを提供できる最適化です。ページロードが高速化されます。

完全ルートキャッシュの仕組みを理解するには、Reactがレンダリングをどのように処理するか、およびNext.jsが結果をキャッシュする方法を見ることが役立ちます。

1. サーバーでのReactレンダリング

サーバーでは、Next.jsはReactのAPIを使用してレンダリングをオーケストレーションします。レンダリング作業はチャンク、つまり個別のルートセグメントとサスペンス境界に分割されます。

各チャンクは2つのステップでレンダリングされます。

  1. Reactはサーバーコンポーネントを、ストリーミング用に最適化された特殊なデータ形式であるReact Server Component Payloadにレンダリングします。
  2. Next.jsはReact Server Component PayloadとクライアントコンポーネントのJavaScript命令を使用して、サーバーでHTMLをレンダリングします。

これは、すべてがレンダリングされるのを待つ前に、作業をキャッシュしたり、レスポンスを送信したりする必要がないことを意味します。代わりに、作業が完了するにつれてレスポンスをストリーミングできます。

React Server Component Payloadとは?

React Server Component Payloadは、レンダリングされたReact Server Componentツリーのコンパクトなバイナリ表現です。これはクライアントのReactによってブラウザのDOMを更新するために使用されます。React Server Component Payloadには以下が含まれます。

  • サーバーコンポーネントのレンダリング結果
  • クライアントコンポーネントがレンダリングされるべき場所のプレースホルダーとそれらのJavaScriptファイルへの参照
  • サーバーコンポーネントからクライアントコンポーネントに渡されるすべてのプロップ

詳しくは、Server Componentsドキュメントをご覧ください。

2. サーバーでのNext.jsキャッシング(完全ルートキャッシュ)

静的にレンダリングされたルートに対して、React Server Component PayloadとHTMLがサーバーにキャッシュされる完全ルートキャッシュのデフォルト動作。

Next.jsのデフォルト動作は、ルートのレンダリング結果(React Server Component PayloadとHTML)をサーバーでキャッシュすることです。これはビルド時に静的にレンダリングされたルート、または再検証中に適用されます。

3. クライアントでのReactハイドレーションと協調

リクエスト時に、クライアント上で

  1. HTMLは、クライアントとサーバーコンポーネントの高速な非インタラクティブな初期プレビューを即座に表示するために使用されます。
  2. React Server Component Payloadは、クライアントとレンダリング済みサーバーコンポーネントツリーを協調させるために使用され、DOMが更新されます。
  3. JavaScript命令は、クライアントコンポーネントをハイドレートし、アプリケーションをインタラクティブにするために使用されます。

4. クライアントでのNext.jsキャッシング(ルーターキャッシュ)

React Server Component Payloadはクライアント側のルーターキャッシュに格納されます。これは個別のルートセグメントに分割されたインメモリキャッシュです。このルーターキャッシュは、訪問済みのルートを保存し、将来のルートをプリフェッチすることで、ナビゲーション体験を向上させるために使用されます。

5. 後続のナビゲーション

後続のナビゲーションまたはプリフェッチ中に、Next.jsはReact Server Component Payloadがルーターキャッシュに格納されているかチェックします。そうである場合、新しいリクエストをサーバーに送信することをスキップします。

ルートセグメントがキャッシュにない場合、Next.jsはサーバーからReact Server Component Payloadをフェッチし、クライアント上のルーターキャッシュを設定します。

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

ルートがビルド時にキャッシュされるかどうかは、静的または動的にレンダリングされるかどうかによって異なります。静的ルートはデフォルトでキャッシュされ、動的ルートはリクエスト時にレンダリングされ、キャッシュされません。

この図は、キャッシュされたデータとキャッシュされていないデータを持つ、静的にレンダリングされたルートと動的にレンダリングされたルートの違いを示します。

静的レンダリングと動的レンダリングが完全ルートキャッシュにどのように影響するかを示す図。静的ルートはビルド時またはデータ再検証後にキャッシュされ、動的ルートはキャッシュされることはありません

静的レンダリングと動的レンダリングの詳細をご覧ください。

期間

デフォルトでは、完全ルートキャッシュは永続的です。つまり、レンダリング出力はユーザーリクエスト間でキャッシュされます。

無効化

完全ルートキャッシュを無効化する方法は2つあります。

  • データ再検証データキャッシュを再検証すると、サーバー上でコンポーネントを再レンダリングし、新しいレンダリング出力をキャッシュしてルーターキャッシュが無効化されます。
  • 再デプロイ:データキャッシュは再デプロイ間で永続的であるのとは異なり、完全ルートキャッシュは新しいデプロイで削除されます。

オプトアウト

完全ルートキャッシュをオプトアウトするか、つまり受信した各リクエストのコンポーネントを動的にレンダリングするには、以下を実行できます。

  • Dynamic APIを使用する:これにより、ルートが完全ルートキャッシュからオプトアウトされ、リクエスト時に動的にレンダリングされます。データキャッシュは引き続き使用できます。
  • dynamic = 'force-dynamic'またはrevalidate = 0ルートセグメント設定オプションを使用する:これにより、完全ルートキャッシュとデータキャッシュがスキップされます。つまり、コンポーネントはレンダリングされ、データは受信した各リクエストでサーバーにフェッチされます。ルーターキャッシュはクライアント側キャッシュであるため、引き続き適用されます。
  • データキャッシュからのオプトアウト:ルートにキャッシュされていないfetchリクエストがある場合、これにより、ルートが完全ルートキャッシュからオプトアウトされます。特定のfetchリクエストのデータは、受信した各リクエストでフェッチされます。明示的にキャッシングを有効にする他のfetchリクエストは、データキャッシュに引き続きキャッシュされます。これにより、キャッシュされたデータとキャッシュされていないデータのハイブリッドが可能になります。

クライアント側ルーターキャッシュ

Next.jsは、レイアウト、ローディング状態、ページに分割されたルートセグメントのRSCペイロードを保存するインメモリ、クライアント側のルーターキャッシュを備えています。

ユーザーがルート間をナビゲートすると、Next.jsは訪問済みのルートセグメントをキャッシュし、ユーザーがナビゲートする可能性が高いルートをプリフェッチします。これにより、インスタント戻る/進む、ナビゲーション間での全ページリロードなし、共有レイアウトのブラウザ状態とReact状態の保持が実現されます。

ルーターキャッシュでは

  • レイアウトはキャッシュされ、ナビゲーション時に再利用されます(部分レンダリング)。
  • ローディング状態はキャッシュされ、ナビゲーション時に再利用されます(インスタントナビゲーション)。
  • ページはデフォルトではキャッシュされませんが、ブラウザの戻る・進むナビゲーション中に再利用されます。試験的なstaleTimes設定オプションを使用して、ページセグメントのキャッシングを有効化できます。

補足:このキャッシュはNext.jsおよびサーバーコンポーネントに特に適用され、ブラウザのbfcacheとは異なりますが、同様の結果をもたらします。

期間

キャッシュはブラウザの一時メモリに格納されます。2つの要因がルーターキャッシュがどの程度の期間保持されるかを決定します。

  • セッション:キャッシュはナビゲーション間で永続化されます。ただし、ページリフレッシュで削除されます。
  • 自動無効化期間:レイアウトとローディング状態のキャッシュは、特定の時間後に自動的に無効化されます。期間は、リソースがどのようにプリフェッチされたか、およびリソースが静的に生成されたかによって異なります。
    • デフォルトプリフェッチprefetch={null}または指定なし):動的ページではキャッシュされません、静的ページでは5分。
    • 完全プリフェッチprefetch={true}またはrouter.prefetch):静的ページと動的ページの両方で5分。

ページリフレッシュはキャッシュされたすべてのセグメントをクリアしますが、自動無効化期間はプリフェッチ時から個別のセグメントのみに影響します。

補足:試験的なstaleTimes設定オプションを使用して、上記の自動無効化時間を調整できます。

無効化

ルーターキャッシュを無効化する方法は2つあります。

  • Server Action内
    • (revalidatePath)でパスごとにオンデマンドデータを再検証、または(revalidateTag)でキャッシュタグごとに再検証
    • (cookies.set)または(cookies.delete)を使用してルーターキャッシュを無効化し、クッキーを使用するルートが古くなるのを防ぎます(例、認証)。
  • router.refreshを呼び出すと、ルーターキャッシュが無効化され、現在のルートに対するサーバーへの新しいリクエストが行われます。

オプトアウト

Next.js 15以降、ページセグメントはデフォルトでオプトアウトされています。

補足<Link>コンポーネントのprefetchプロップをfalseに設定することで、プリフェッチもオプトアウトできます。

キャッシュの相互作用

異なるキャッシング機構を設定する場合、それらが相互にどのように相互作用するかを理解することが重要です。

データキャッシュと完全ルートキャッシュ

  • データキャッシュを再検証またはオプトアウトすると、レンダリング出力がデータに依存するため、完全ルートキャッシュが無効化されます
  • 完全ルートキャッシュを無効化またはオプトアウトすると、データキャッシュに影響しません。キャッシュされたデータとキャッシュされていないデータの両方を持つルートを動的にレンダリングできます。これは、ページのほとんどがキャッシュされたデータを使用するが、リクエスト時にフェッチする必要があるデータに依存する少数のコンポーネントがある場合に役立ちます。すべてのデータを再フェッチするパフォーマンスへの影響について心配することなく、動的にレンダリングできます。

データキャッシュとクライアント側ルーターキャッシュ

  • データキャッシュとルーターキャッシュを即座に無効化するには、Server ActionrevalidatePathまたはrevalidateTagを使用できます。
  • Route Handlerでデータキャッシュを再検証すると、ルートハンドラーは特定のルートに結び付けられていないため、ルーターキャッシュは即座に無効化されません。これは、ルーターキャッシュがハードリフレッシュまたは自動無効化期間が経過するまで、前のペイロードを提供し続けることを意味します。

API

次の表は、異なるNext.js APIがキャッシングにどのように影響するかの概要を示します。

APIルーターキャッシュ完全ルートキャッシュデータキャッシュReactキャッシュ
<Link prefetch>キャッシュ
router.prefetchキャッシュ
router.refresh再検証
fetchキャッシュキャッシュ(GETおよびHEAD)
fetch options.cacheキャッシュまたはオプトアウト
fetch options.next.revalidate再検証再検証
fetch options.next.tagsキャッシュキャッシュ
revalidateTag再検証(Server Action)再検証再検証
revalidatePath再検証(Server Action)再検証再検証
const revalidate再検証またはオプトアウト再検証またはオプトアウト
const dynamicキャッシュまたはオプトアウトキャッシュまたはオプトアウト
cookies再検証(Server Action)オプトアウト
headerssearchParamsオプトアウト
generateStaticParamsキャッシュ
React.cacheキャッシュ
unstable_cacheキャッシュ

デフォルトでは、<Link>コンポーネントは自動的にルートを完全ルートキャッシュからプリフェッチし、React Server Component Payloadをルーターキャッシュに追加します。

プリフェッチを無効化するには、prefetchプロップをfalseに設定できます。ただし、これはキャッシュを永続的にスキップしません。ユーザーがルートを訪問するとき、ルートセグメントは引き続きクライアント側でキャッシュされます。

<Link>コンポーネントの詳細をご覧ください。

router.prefetch

useRouterフックのprefetchオプションは、ルートを手動でプリフェッチするために使用できます。これはReact Server Component PayloadをルーターキャッシュにAdd。

useRouterフックAPIリファレンスをご覧ください。

router.refresh

useRouterフックのrefreshオプションは、ルートを手動で更新するために使用できます。これはルーターキャッシュを完全にクリアし、現在のルートに対するサーバーへの新しいリクエストを行います。refreshはデータキャッシュまたは完全ルートキャッシュに影響しません。

レンダリング結果はReact状態とブラウザ状態を保持しながら、クライアント側で協調されます。

useRouterフックAPIリファレンスをご覧ください。

fetch

fetchから返されたデータは、データキャッシュに_自動的にキャッシュされません_。

デフォルトでは、cacheまたはnext.revalidateオプションが提供されていない場合。

詳細オプションについては、fetch APIリファレンスをご覧ください。

fetch options.cache

cacheオプションをforce-cacheに設定することで、個別のfetchをキャッシングにオプトインできます。

// キャッシングにオプトイン
fetch(`https://...`, { cache: 'force-cache' })

詳細オプションについては、fetch APIリファレンスをご覧ください。

fetch options.next.revalidate

fetchnext.revalidateオプションを使用して、個別のfetchリクエストの再検証期間(秒単位)を設定できます。これにより、データキャッシュが再検証され、それに応じて完全ルートキャッシュが再検証されます。新しいデータがフェッチされ、コンポーネントがサーバーで再レンダリングされます。

// 最大1時間後に再検証
fetch(`https://...`, { next: { revalidate: 3600 } })

詳細オプションについては、fetch APIリファレンスをご覧ください。

fetch options.next.tagsおよびrevalidateTag

Next.jsは、きめ細かいデータキャッシングと再検証のためのキャッシュタグシステムを備えています。

  1. fetchまたはunstable_cacheを使用する場合、キャッシュエントリに1つ以上のタグでタグ付けするオプションがあります。
  2. その後、revalidateTagを呼び出して、そのタグに関連するキャッシュエントリを削除できます。

たとえば、データをフェッチするときにタグを設定できます。

// タグでデータをキャッシュ
fetch(`https://...`, { next: { tags: ['a', 'b', 'c'] } })

その後、revalidateTagをタグで呼び出してキャッシュエントリを削除します。

// 特定のタグでエントリを再検証
revalidateTag('a')

達成したいことに応じて、revalidateTagを使用できる場所は2つあります。

  1. Route Handlers:サードパーティイベント(例、ウェブフック)の応答でデータを再検証します。ルートハンドラーは特定のルートに結び付けられていないため、これはルーターキャッシュを即座に無効化しません。
  2. Server Actions:ユーザーアクション(例、フォーム送信)の後、データを再検証します。これにより、関連するルートのルーターキャッシュが無効化されます。

revalidatePath

revalidatePathを使用して、特定のパス下のルートセグメントをデータ再検証して再レンダリングを手動で行い、1つの操作で実行できます。revalidatePathメソッドを呼び出すと、データキャッシュが再検証され、それに応じて完全ルートキャッシュが無効化されます。

revalidatePath('/')

達成したいことに応じて、revalidatePathを使用できる場所は2つあります。

  1. Route Handlers:サードパーティイベント(例、ウェブフック)の応答でデータを再検証します。
  2. Server Actions:ユーザーインタラクション(例、フォーム送信、ボタンのクリック)の後、データを再検証します。

詳細については、revalidatePath APIリファレンスをご覧ください。

revalidatePath vs. router.refresh

router.refreshを呼び出すと、ルーターキャッシュがクリアされ、データキャッシュまたは完全ルートキャッシュを無効化せずに、サーバー上でルートセグメントが再レンダリングされます。

違いは、revalidatePathがデータキャッシュと完全ルートキャッシュを削除するのに対し、router.refresh()はクライアント側APIであるため、データキャッシュと完全ルートキャッシュを変更しないことです。

Dynamic API

cookiesheadersなどのDynamic API、およびページ内のsearchParamsプロップは、実行時の受信リクエスト情報に依存します。それらを使用すると、ルートが完全ルートキャッシュからオプトアウトされます。つまり、ルートは動的にレンダリングされます。

cookies

Server Action内でcookies.setまたはcookies.deleteを使用すると、クッキーを使用するルートが古くなるのを防ぐため、ルーターキャッシュが無効化されます(例、認証の変更を反映するため)。

cookies APIリファレンスをご覧ください。

セグメント設定オプション

ルートセグメント設定オプションは、ルートセグメントのデフォルトをオーバーライドするか、fetch API(例、データベースクライアントまたはサードパーティライブラリ)を使用できない場合に使用できます。

次のルートセグメント設定オプションは、完全ルートキャッシュからオプトアウトします。

  • const dynamic = 'force-dynamic'

このコンフィグオプションは、すべてのフェッチをデータキャッシュからオプトアウトします(例、no-store)。

  • const fetchCache = 'default-no-store'

より多くの高度なオプションは、fetchCacheをご覧ください。

その他のオプションについては、ルートセグメント設定ドキュメントをご覧ください。

generateStaticParams

動的セグメント(例、app/blog/[slug]/page.js)について、generateStaticParamsによって提供されるパスはビルド時に完全ルートキャッシュにキャッシュされます。リクエスト時に、Next.jsはビルド時に認識されていないパスも初めて訪問されるときにキャッシュします。

ビルド時にすべてのパスを静的にレンダリングするには、パスの完全なリストをgenerateStaticParamsに提供します。

app/blog/[slug]/page.js
export async function generateStaticParams() {
  const posts = await fetch('https://.../posts').then((res) => res.json())
 
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

ビルド時にパスのサブセットを静的にレンダリングし、残りを実行時に初めて訪問されるときにレンダリングするには、パスの部分的なリストを返します。

app/blog/[slug]/page.js
export async function generateStaticParams() {
  const posts = await fetch('https://.../posts').then((res) => res.json())
 
  // 最初の10個の投稿をビルド時にレンダリング
  return posts.slice(0, 10).map((post) => ({
    slug: post.slug,
  }))
}

訪問されるすべてのパスを初めて静的にレンダリングするには、空の配列を返します(ビルド時にパスはレンダリングされません)、またはexport const dynamic = 'force-static'を活用します。

app/blog/[slug]/page.js
export async function generateStaticParams() {
  return []
}

補足generateStaticParamsから配列を返す必要があります。空の場合でも、そうでない場合は、ルートは動的にレンダリングされます。

app/changelog/[slug]/page.js
export const dynamic = 'force-static'

リクエスト時にキャッシングを無効化するには、export const dynamicParams = falseオプションをルートセグメントに追加します。このコンフィグオプションを使用した場合、generateStaticParamsによって提供されるパスのみが提供され、他のルートは404またはマッチします(catch-allルートの場合)。

React cache関数

React cache関数を使用して、関数の戻り値をメモ化でき、同じ関数を複数回呼び出しながら一度だけ実行できます。

GETまたはHEADメソッドを使用するfetchリクエストは自動的にメモ化されるため、React cacheでラップする必要はありません。ただし、他のfetchメソッド、またはデータ取得ライブラリ(例、一部のデータベース、CMS、またはGraphQLクライアント)がリクエストをネイティブにメモ化しない場合、cacheを使用してデータリクエストを手動でメモ化できます。

utils/get-item.ts
TypeScript
import { cache } from 'react'
import db from '@/lib/db'
 
export const getItem = cache(async (id: string) => {
  const item = await db.item.findUnique({ id })
  return item
})