メインコンテンツまでスキップ

use cache

use cacheディレクティブは、コンポーネント、関数、またはファイルをキャッシュ対象として指定します。ファイルの先頭に使用することで、そのファイル内のすべての関数がキャッシュ可能であることを示したり、関数の先頭にインラインで使用してその関数をキャッシュ可能としてマークしたりできます。これは実験的なNext.jsの機能であり、use clientuse serverのようなネイティブのReact機能ではありません。

next.config.tsファイルでdynamicIOフラグを使用してuse cacheディレクティブのサポートを有効にします:

next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
}

export default nextConfig

将来的にuse cacheディレクティブはdynamicIOフラグとは別に提供されます。

キャッシュは、Webアプリケーションのパフォーマンスを向上させるために、レンダリングやデータリクエストの結果を保存する技術です。非同期関数やリクエスト時のデータに依存するAPIを使用するたびに、Next.jsは自動的に動的レンダリングを選択します。これらの操作の結果を明示的にキャッシュし、use cacheディレクティブを使用してアプリケーションのレンダリングパフォーマンスを最適化できます。

use cacheディレクティブはunstable_cache関数の代替を目的とした実験的な機能です。unstable_cacheはJSONデータのキャッシュに限られ、再検証期間とタグの手動定義が必要ですが、use cacheはより柔軟で、React Server Components (RSC) がシリアライズ可能なデータ、データフェッチの出力、コンポーネントの出力をキャッシュできます。

さらに、use cacheは入力と出力の両方の追跡を行うことでキャッシュの複雑さを自動的に管理し、誤ってキャッシュを汚染するリスクを軽減します。入力と出力の両方をシリアライズするため、誤ったキャッシュ取得に関する問題を回避できます。

use cacheディレクティブ

Next.jsのuse cacheディレクティブを使用すると、ルート全体、コンポーネント、および関数の戻り値をキャッシュできます。非同期関数を持つとき、ファイルの先頭または関数スコープ内にuse cacheを追加してキャッシュ可能にマークできます。これにより、Next.jsは戻り値をキャッシュし、後続のレンダリングで再利用できることを認識します。

// ファイルレベル
'use cache'

export default async function Page() {
// ...
}

// コンポーネントレベル
export async function MyComponent() {
'use cache'
return <></>
}

// 関数レベル
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}

Good to know: use cacheディレクティブを使用する関数は、状態の変更、DOMの直接操作、または一定の間隔でコードを実行するためのタイマーの設定などの副作用を持たない必要があります

再検証

デフォルトで、Next.jsはuse cacheディレクティブを使用する際に 15分の再検証期間 を設定します。Next.jsはほぼ無限の有効期限を持つように設定されており、頻繁な更新が不要なコンテンツに適しています。

この再検証期間は頻繁な変更が不要なコンテンツに便利ですが、cacheLifeおよびcacheTagAPIを使用してキャッシュの挙動を設定できます:

  • cacheLife: 時間ベースの再検証期間に使用
  • cacheTag: オンデマンドでの再検証に使用

これらのAPIはクライアントとサーバーのキャッシングレイヤ間で統合されているため、キャッシュのセマンティクスを1か所で設定することで、全体に適用できます

基本的な例

以下の例では、関数のレベルでcacheLife関数を使用して、関数の出力に1日の再検証期間を設定する方法を示します:

app/components/my-component.tsx
import { unstable_cacheLife as cacheLife } from 'next/cache'

export async function MyComponent() {
async function getData() {
'use cache'
cacheLife('days')
const data = await fetch('/api/data')
return data
}

return // ここでデータを使用
}

キャッシュ再検証の仕組み

15分の再検証期間が設定されている場合、以下のようなことが起こります:

  1. Cache HIT: 15分以内にリクエストが行われると、キャッシュされたデータが提供され、キャッシュHITとなります。
  2. 古いデータ: 15分後にリクエストがあると、キャッシュされた値は提供されますが、現在は古くなっています。Next.jsはバックグラウンドで新しいキャッシュエントリを再計算します。
  3. Cache MISS: キャッシュエントリが期限切れになり、その後にリクエストが行われると、Next.jsはこれをキャッシュMISSとして扱い、データが再計算され、ソースから再度フェッチされます。

cacheLifeによる時間ベースの再検証

cacheLife関数はuse cacheディレクティブがある場所でのみ使用でき、キャッシュプロファイルに基づいた時間ベースの再検証期間を定義できます。

use cacheディレクティブを使用する際には、キャッシュの挙動を明示的に定義するために、常にキャッシュプロファイルを追加することをお勧めします。

キャッシュプロファイルは次のプロパティを含むオブジェクトです:

プロパティ説明要件
stalenumberクライアントがサーバーに確認せずに値をキャッシュする期間任意
revalidatenumberサーバーでキャッシュを更新する頻度; 古い値は再検証中に提供されることがあります任意
expirenumber動的フェッチに切り替える前に古くなった値が残る最大期間; revalidateより長くなければならない任意 - revalidateより長くなければならない

"stale"プロパティはstaleTimes設定とは異なり、クライアント側のルーターキャッシュを特に制御します。staleTimesは動的データと静的データのすべてのインスタンスに影響を与えるグローバル設定ですが、cacheLife構成は、関数またはルートごとに"古い"時間を定義することができます。

Good to know: “stale”プロパティはCache-control: max-ageヘッダーを設定しません。代わりに、クライアント側のルーターキャッシュを制御します。

デフォルトのキャッシュプロファイル

Next.jsはさまざまな時間軸を基にした一連の名前付きキャッシュプロファイルを提供します。use cacheディレクティブと一緒にcacheLife関数でキャッシュプロファイルを指定しない場合、Next.jsは自動的に“デフォルト”のキャッシュプロファイルを適用します。

プロファイルStaleRevalidateExpire説明
defaultundefined15分INFINITE_CACHE頻繁に更新が必要ないコンテンツに適したデフォルトプロファイル
secondsundefined1秒1分ほぼリアルタイムの更新が必要な急速に変化するコンテンツ用
minutes5分1分1時間1時間以内に頻繁に更新されるコンテンツ用
hours5分1時間1日毎日更新されるが少し古くても大丈夫なコンテンツ用
days5分1日1週間毎週更新されるが1日経過しても大丈夫なコンテンツ用
weeks5分1週間1ヶ月毎月更新されるが1週間経過しても大丈夫なコンテンツ用
max5分1ヶ月INFINITE_CACHE変更がほとんど不要な非常に安定したコンテンツ用

基本的な例

app/page.tsx
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'

cacheLife('minutes')

キャッシュプロファイルを参照するために使われる文字列の値は固有の意味を持ちません;それらは意味論的なラベルとして機能します。これにより、コードベース内のキャッシュされたコンテンツをよりよく理解し、管理することができます。

再利用可能なキャッシュプロファイルの定義

next.config.tsファイルにキャッシュプロファイルを定義することで再利用可能なキャッシュプロファイルを作成できます。使用するケースに適した名前を選択し、stalerevalidateexpireプロパティに値を設定します。必要に応じて任意の数のカスタムキャッシュプロファイルを作成できます。各プロフィールは、cacheLife関数に渡す文字列値として名前を使用することで参照できます。

next.config.ts
const nextConfig = {
experimental: {
dynamicIO: true,
cacheLife: {
biweekly: {
stale: 60 * 60 * 24 * 14, // 14日
revalidate: 60 * 60 * 24, // 1日
expire: 60 * 60 * 24 * 14, // 14日
},
},
},
}

module.exports = nextConfig

上記の例では、14日間キャッシュされ、毎日更新をチェックし、14日後にキャッシュが期限切れになります。このプロフィールはアプリケーション全体で名前を基に参照できます:

app/page.tsx
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'

cacheLife('biweekly')

// 以降のコード

デフォルトのキャッシュプロファイルのオーバーライド

デフォルトのキャッシュプロファイルは、キャッシュ可能な出力の鮮度や古さを考えるのに役立ちますが、アプリケーションのキャッシング戦略により適した異なる名前付きのプロファイルを使用したい場合があります。

デフォルトの名前付きキャッシュプロファイルをオーバーライドするには、デフォルトと同じ名前で新しい構成を作成します。

以下の例は、デフォルトの“days”キャッシュプロファイルをオーバーライドする方法を示しています:

next.config.ts
const nextConfig = {
experimental: {
dynamicIO: true,
cacheLife: {
days: {
stale: 3600, // 1時間
revalidate: 900, // 15分
expire: 86400, // 1日
},
},
},
}

module.exports = nextConfig

インラインでのキャッシュプロファイル定義

特定のユースケースには、cacheLife関数にオブジェクトを渡すことでカスタムキャッシュプロファイルを設定できます:

app/page.tsx
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'

cacheLife({
stale: 3600, // 1時間
revalidate: 900, // 15分
expire: 86400, // 1日
})

// 以降のコード

このインラインキャッシュプロファイルはそれが作成された関数またはファイルにのみ適用されます。同じプロファイルをアプリケーション全体で再利用したい場合は、next.config.tsファイルのcacheLifeプロパティに構成を追加できます。

use cachecacheLifeのネストした使用

同じルートまたはコンポーネントツリーで複数のキャッシング動作を定義する場合、内部キャッシュが独自のcacheLifeプロファイルを指定している場合、外部キャッシュはそれらの中で最も短いキャッシュ期間を尊重します。これは外部キャッシュが自分の明示的なcacheLifeプロファイルを持たない場合のみ適用されます。

キャッシュ境界の決定階層:

  1. Next.jsは、内部のuse cacheディレクティブを除いて、use cache境界内の最短キャッシュプロファイルを使用します。
  2. キャッシュプロファイルがない場合は、すべての内部use cache呼び出しからの最短プロファイル時間がこのuse cacheに適用されます。内部のuse cacheがない場合はデフォルトが使用されます。
  3. 2レベル深くの内部キャッシュは外部キャッシュに影響を与えません;彼らは既に親に自身の期間を提供しているためです。

例として、ページにキャッシュプロファイルを指定せずにuse cacheディレクティブを追加すると、デフォルトのキャッシュプロファイルが暗黙的に適用されます(cacheLife("default"))。ページにインポートされたコンポーネントもuse cacheディレクティブを使用し、独自のキャッシュプロファイルを持つ場合、外部と内部キャッシュプロファイルが比較され、プロファイルに設定された最短期間が適用されます。

app/components/parent.tsx
// 親コンポーネント
import { unstable_cacheLife as cacheLife } from 'next/cache'
import { ChildComponent } from './child'

export async function ParentComponent() {
'use cache'
cacheLife('days')

return (
<div>
<ChildComponent />
</div>
)
}

別のファイルで、インポートされた子コンポーネントを定義します:

app/components/child.tsx
// 子コンポーネント
import { unstable_cacheLife as cacheLife } from 'next/cache'

export async function ChildComponent() {
'use cache'
cacheLife('hours')
return <div>Child Content</div>

// このコンポーネントのキャッシュは、短い"hours"プロファイルを尊重します
}

cacheTagによるオンデマンド再検証

cacheTagrevalidateTagと組み合わせて、必要に応じてキャッシュデータをパージするために使用されます。cacheTag関数は単一の文字列値または文字列配列を受け取ります。

次の例では、getData関数が“weeks”キャッシュプロファイルを使用し、関数のキャッシュされた出力にcacheTagを定義しています:

app/actions.ts
import {
unstable_cacheTag as cacheTag,
unstable_cacheLife as cacheLife,
} from 'next/cache'

export async function getData() {
'use cache'
cacheLife('weeks')
cacheTag('my-data')

const data = await fetch('/api/data')
return data
}

その後、任意の関数でrevalidateTagAPIを使用してオンデマンドでキャッシュをパージできます。たとえば、route handlerServer Actionです:

app/submit.ts
'use server'

import { revalidateTag } from 'next/cache'

export default async function submit() {
await addPost()
revalidateTag('my-data')
}

オンデマンドでのキャッシュデータのパージに関する詳細は、revalidateTagドキュメントを参照してください。

use cacheによるルート全体のキャッシュ

アプリケーション内のSuspense境界の配置は、コンポーネントの動的性を決定します。Suspense境界内のコンポーネントは動的であることが許可されていますが、自動的に動的であるわけではありません。すべてをキャッシュまたはコンテンツが静的である場合、Next.jsは静的なアプリケーションを生成します。Suspenseを使用することで、境界内で動的な動作が許可されていることを示します。

ルートが静的であることを保証するためには、Suspense境界を使用しないでください。使用する必要がある場合は、レイアウトとページコンポーネントの両方にuse cacheディレクティブを追加することで、アプリケーション内での個別のエントリーポイントとして扱うことができます。

これは以前export const dynamic = "force-cache"オプションを使用していたアプリケーションに推奨され、ルート全体がプリレンダリングされます。

app/layout.tsx
"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'
cacheLife('minutes')

export default Layout({children}: {children: ReactNode}) {
return <div>{children}</div>
}

そして、page.tsxファイルでは、ファイルの先頭にuse cacheディレクティブを追加し、キャッシュプロファイルを定義できます:

app/page.tsx
"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'
cacheLife('minutes')

async function Users() {
const users = await fetch('/api/users');
// ユーザーをループ
}

export default Page() {
return (
<main>
<Users/>
</main>
)
}

use cacheによるコンポーネント出力のキャッシュ

コンポーネントレベルでuse cacheを使用して、そのコンポーネント内で行われるフェッチや計算をキャッシュできます。アプリケーション内でコンポーネントを再利用する際、propsの構造が同じであれば、同じキャッシュエントリを共有できます。

propsはシリアライズされ、キャッシュキーの一部を形成します。同じコンポーネントをアプリケーションの複数の箇所で使用する場合、シリアライズされたpropsが各インスタンスで同じ値を生成する限り、キャッシュエントリは再利用されます。

app/components/bookings.tsx
import { unstable_cacheLife as cacheLife } from 'next/cache'

interface BookingsProps {
type: string
}

export async function Bookings({ type = 'massage' }: BookingsProps) {
'use cache'
cacheLife('minutes')

async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}

use cacheによる関数出力のキャッシュ

非同期関数ならどこにでもuse cacheを追加できるので、コンポーネントやルートのキャッシュに限定されません。ネットワークリクエストやデータベースクエリをキャッシュしたい場合や、非常に遅い計算を行いたい場合があります。この種の作業を含む関数にuse cacheを追加することでキャッシュ可能になり、再利用されると同じキャッシュエントリを共有します。

app/actions.ts
import { unstable_cacheLife as cacheLife } from 'next/cache'

export async function getData() {
'use cache'
cacheLife('minutes')

const data = await fetch('/api/data')
return data
}