use cache
use cache
ディレクティブを使用すると、ルート、Reactコンポーネント、または関数をキャッシュ可能としてマークできます。ファイルの先頭で使用して、そのファイル内のすべてのエクスポートをキャッシュすることを示したり、関数やコンポーネントの先頭でインラインで使用して、戻り値をキャッシュすることができます。
使用法
use cache
は現在、実験的な機能です。これを有効にするには、next.config.ts
ファイルにuseCache
オプションを追加します:
- TypeScript
- JavaScript
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
useCache: true,
},
}
export default nextConfig
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
useCache: true,
},
}
module.exports = nextConfig
Good to know:
use cache
はdynamicIO
オプションでも有効にできます。
次に、ファイル、コンポーネント、または関数レベルでuse cache
を追加します:
// ファイルレベル
'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
の動作
キャッシュキー
キャッシュエントリのキーは、その入力のシリアライズされたバージョンを使用して生成されます。これには以下が含まれます:
- ビルドID(各ビルドで生成される)
- 関数ID(関数に固有のセキュアな識別子)
- シリアライズ可能な関数の引数(またはprops)
キャッシュされた関数に渡される引数や、親スコープから読み取る値は自動的にキーの一部になります。つまり、入力が同じである限り、同じキャッシュエントリが再利用されます。
非シリアライズ可能な引数
非シリアライズ可能な引数、props、または閉じ込められた値は、キャッシュされた関数内で参照に変わり、通過させることはできますが、検査や変更はできません。これらの非シリアライズ可能な値はリクエスト時に埋め込まれ、キャッシュキーの一部にはなりません。
たとえば、キャッシュされた関数はJSXをchildren
propとして受け取り、<div>{children}</div>
を返すことができますが、実際のchildren
オブジェクトを内省することはできません。これにより、キャッシュされたコンポーネント内にキャッシュされていないコンテンツをネストすることができます。
- TypeScript
- JavaScript
function CachedComponent({ children }: { children: ReactNode }) {
'use cache'
return <div>{children}</div>
}
function CachedComponent({ children }) {
'use cache'
return <div>{children}</div>
}
戻り値
キャッシュ可能な関数の戻り値はシリアライズ可能でなければなりません。これにより、キャッシュされたデータが正しく保存および取得されることが保証されます。
ビルド時のuse cache
layoutやpageの先頭で使用すると、ルートセグメントがプリレンダリングされ、後で再検証できるようになります。
つまり、use cache
はcookies
やheaders
のようなリクエスト時APIと一緒に使用することはできません。
実行時のuse cache
サーバーでは、個々のコンポーネントや関数のキャッシュエントリがメモリ内にキャッシュされます。
その後、クライアントでは、サーバーキャッシュから返されたコンテンツがセッションの間、または再検証されるまでブラウザのメモリに保存されます。
再検証中
デフォルトでは、use cache
はサーバー側の再検証期間が15分です。この期間は頻繁な更新を必要としないコンテンツに役立ちますが、cacheLife
とcacheTag
APIを使用して、個々のキャッシュエントリが再検証されるタイミングを設定できます。
これらのAPIはクライアントとサーバーのキャッシングレイヤー全体で統合されており、1か所でキャッシングのセマンティクスを設定し、どこでも適用されるようにできます。
cacheLife
とcacheTag
のAPIドキュメントを参照してください。
例
use cache
を使用したルート全体のキャッシュ
ルート全体をプリレンダリングするには、layout
とpage
ファイルの両方の先頭にuse cache
を追加します。これらの各セグメントはアプリケーションの個別のエントリーポイントとして扱われ、独立してキャッシュされます。
- TypeScript
'use cache'
export default function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
}
- JavaScript
'use cache'
export default function Layout({ children }) {
return <div>{children}</div>
}
page
ファイルにインポートされネストされたコンポーネントは、page
のキャッシュ動作を継承します。
- TypeScript
- JavaScript
'use cache'
async function Users() {
const users = await fetch('/api/users')
// ユーザーをループ処理
}
export default function Page() {
return (
<main>
<Users />
</main>
)
}
'use cache'
async function Users() {
const users = await fetch('/api/users')
// ユーザーをループ処理
}
export default function Page() {
return (
<main>
<Users />
</main>
)
}
Good to know:
use cache
がlayout
またはpage
のみに追加された場合、そのルートセグメントとそこにインポートされたコンポーネントのみがキャッシュされます。- ルート内のネストされた子がDynamic APIsを使用している場合、ルートはプリレンダリングから除外されます。
use cache
を使用したコンポーネントの出力のキャッシュ
コンポーネントレベルでuse cache
を使用して、そのコンポーネント内で行われるフェッチや計算をキャッシュできます。シリアライズされたpropsが各インスタンスで同じ値を生成する限り、キャッシュエントリは再利用されます。
- TypeScript
- JavaScript
export async function Bookings({ type = 'haircut' }: BookingsProps) {
'use cache'
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
interface BookingsProps {
type: string
}
export async function Bookings({ type = 'haircut' }) {
'use cache'
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
use cache
を使用した関数の出力のキャッシュ
非同期関数にuse cache
を追加できるため、コンポーネントやルートのキャッシュに限定されません。ネットワークリクエスト、データベースクエリ、または遅い計算をキャッシュしたい場合があります。
- TypeScript
- JavaScript
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}
インターリービング
キャッシュ可能な関数に非シリアライズ可能な引数を渡す必要がある場合は、それらをchildren
として渡すことができます。これにより、children
の参照が変更されてもキャッシュエントリに影響を与えません。
- TypeScript
- JavaScript
export default async function Page() {
const uncachedData = await getData()
return (
<CacheComponent>
<DynamicComponent data={uncachedData} />
</CacheComponent>
)
}
async function CacheComponent({ children }: { children: ReactNode }) {
'use cache'
const cachedData = await fetch('/api/cached-data')
return (
<div>
<PrerenderedComponent data={cachedData} />
{children}
</div>
)
}
export default async function Page() {
const uncachedData = await getData()
return (
<CacheComponent>
<DynamicComponent data={uncachedData} />
</CacheComponent>
)
}
async function CacheComponent({ children }) {
'use cache'
const cachedData = await fetch('/api/cached-data')
return (
<div>
<PrerenderedComponent data={cachedData} />
{children}
</div>
)
}
キャッシュされたコンポーネントを通じてServer ActionsをClient Componentsに渡すこともできますが、キャッシュ可能な関数内でそれらを呼び出すことはありません。
- TypeScript
- JavaScript
import ClientComponent from './ClientComponent'
export default async function Page() {
const performUpdate = async () => {
'use server'
// サーバー側の更新を実行
await db.update(...)
}
return <CacheComponent performUpdate={performUpdate} />
}
async function CachedComponent({
performUpdate,
}: {
performUpdate: () => Promise<void>
}) {
'use cache'
// ここでperformUpdateを呼び出さない
return <ClientComponent action={performUpdate} />
}
import ClientComponent from './ClientComponent'
export default async function Page() {
const performUpdate = async () => {
'use server'
// サーバー側の更新を実行
await db.update(...)
}
return <CacheComponent performUpdate={performUpdate} />
}
async function CachedComponent({ performUpdate }) {
'use cache'
// ここでperformUpdateを呼び出さない
return <ClientComponent action={performUpdate} />
}
- TypeScript
- JavaScript
'use client'
export default function ClientComponent({
action,
}: {
action: () => Promise<void>
}) {
return <button onClick={action}>Update</button>
}
'use client'
export default function ClientComponent({ action }) {
return <button onClick={action}>Update</button>
}