Menu

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 関数を置き換えることを目的とした実験的な機能です。JSON データのキャッシュと再検証期間とタグの手動定義に限定される unstable_cache とは異なり、use cache はより柔軟性があります。React サーバーコンポーネント(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
}

補足: use cache ディレクティブを使用する関数は、状態の変更、DOM の直接操作、またはインターバルでコードを実行するタイマーの設定などの副作用を持ってはいけません。

再検証

デフォルトでは、use cache ディレクティブを使用すると、Next.js は 再検証期間を 15 分に設定します。Next.js はほぼ無限の有効期間を設定するため、頻繁に更新する必要のないコンテンツに適しています。

この再検証期間は、あまり変更されないと予想されるコンテンツには有用ですが、cacheLife および cacheTag API を使用してキャッシュの動作を設定できます:

  • 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. キャッシュ HIT: 15 分以内にリクエストが行われると、キャッシュされたデータが提供され、キャッシュ HIT となります。
  2. 古いデータ: 15 分経過後にリクエストが行われると、キャッシュされた値は依然として提供されますが、古いものとみなされます。Next.js はバックグラウンドで新しいキャッシュエントリを再計算します。
  3. キャッシュ MISS: キャッシュエントリの有効期限が切れ、後続のリクエストが行われると、Next.js はこれをキャッシュ MISS として扱い、データが再度計算され、ソースから再取得されます。

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

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

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

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

プロパティ説明要件
stalenumberクライアントがサーバーに確認せずにキャッシュする値の期間。オプション
revalidatenumberサーバー上でキャッシュを更新する頻度。再検証中は古い値が提供される可能性がある。オプション
expirenumber値が古いままでいられる最大期間。動的フェッチに切り替わる前に、revalidate より長くなければならない。オプション - revalidate より長くする必要あり

「stale」プロパティは、staleTimes 設定とは異なり、クライアントサイドのルーターキャッシュを特に制御します。staleTimes は動的および静的データの両方のインスタンスに影響するグローバル設定であるのに対し、cacheLife 設定を使用すると、関数またはルート単位で「stale」時間を定義できます。

補足: 「stale」プロパティは Cache-control: max-age ヘッダーを設定しません。代わりに、クライアントサイドのルーターキャッシュを制御します。

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

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

プロファイルStaleRevalidateExpire説明
default未定義15 分無限キャッシュデフォルトプロファイル。頻繁に更新する必要のないコンテンツに適切
seconds未定義1 秒1 分ほぼリアルタイムの更新を必要とする、急速に変化するコンテンツ用
minutes5 分1 分1 時間1 時間以内に頻繁に更新されるコンテンツ用
hours5 分1 時間1 日日次で更新されるが、わずかに古くてもよいコンテンツ用
days5 分1 日1 週間週次で更新されるが、1 日古くてもよいコンテンツ用
weeks5 分1 週間1 か月月次で更新されるが、1 週間古くてもよいコンテンツ用
max5 分1 か月無限キャッシュ更新の必要がほとんどない非常に安定したコンテンツ用

基本的な例:

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日間キャッシュし、1日ごとに更新をチェックし、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>子コンテンツ</div>
 
  // このコンポーネントのキャッシュは、より短い 'hours' プロファイルを尊重します
}

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

cacheTag は、キャッシュデータをオンデマンドで削除するために revalidateTag と組み合わせて使用されます。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
}

その後、ルートハンドラまたはサーバーアクションなど、別の関数で revalidateTag APIを使用してオンデマンドでキャッシュを削除できます:

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
}