mcpField
C
cache-components

cache-components

github.com/laguagu開発70214 回インストール2 日前に更新
リポジトリ

cache-components について

Cache Componentsは、プロジェクトの既存設定を維持しながらNext.jsのキャッシュパターンを適用するスキルです。開発者向けに、バージョンごとの挙動、キャッシュ境界、新鮮度、無効化の確認方法と、本番挙動のテスト方法を示します。

主な機能

  1. 01Next.jsのバージョン別キャッシュ指針
  2. 02キャッシュ境界とキーの設計
  3. 03鮮度と無効化のパターン
  4. 04Suspenseとリクエストデータの配置
  5. 05移行とトラブルシューティング
  6. 06本番挙動のテスト項目

活用シーン

  1. 01アプリへのCache Components導入
  2. 02キャッシュ期間と無効化ルールの設定
  3. 03古い表示やルートのブロックを診断
  4. 04本番モードでナビゲーションと更新を検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/laguagu/claude-code-nextjs-skills --skill cache-components

インストール後、Claude Code で/cache-componentsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

PATTERNS.md
REFERENCE.md
TROUBLESHOOTING.md

詳細

キャッシュコンポーネント

移行を求められていない限り、プロジェクトのキャッシュモードを維持してください。これらのパターンを適用する前に、インストールされている Next.js のバージョンと、cacheComponents/partialPrefetching の設定を確認してください。

まず、node_modules/next/dist/docs/ にある関連ガイドを読んでください(16.2 以降は同梱されています。モノレポでは、アプリ固有の next パッケージを使用してください)。Web パスは番号付きフォルダー内のファイルに対応しているため、名前で探してください。たとえば、.../functions/cacheLife は 01-app/03-api-reference/04-functions/cacheLife.md です。01-app/ 以下では、まず 02-guides/migrating-to-cache-components.md、01-getting-started/08-caching.md、02-guides/instant-navigation.md を確認してください。/docs/messages 以下のエラーページは同梱されていません。修正方法は next dev/next build がインラインで表示します。ローカルにドキュメントがない場合は、公式のキャッシュドキュメントを参照してください。正確なエクスポートとシグネチャは node_modules/next/cache.d.ts にも記載されています。16.4 以降の静的出力や段階的な遅延に関する質問では、インストール済みの ensureStatic ルートセグメントリファレンスと、navigation/prefetch 関数のリファレンスを名前で探してください。

目標は、ミューテーション、デプロイ、クライアントナビゲーションの後も鮮度と認可のルールが正しく保たれる、実用的なプリレンダリング済みシェルです。

古い例が誤解させやすい事実

  • Next.js 16 では、トップレベルの cacheComponents: true でこのモードを有効にします。これは experimental.dynamicIO、experimental.useCache、experimental.ppr を置き換えます。 cacheLife/cacheTag は unstable_ なしで next/cache からインポートします。 16.4 では、partialPrefetching を明示的に設定してください。未設定のままにすると警告が表示されます。 推奨される create-next-app のデフォルト設定では、両方のフラグが有効になります。
  • 有効にすると、セグメントのエクスポート dynamic、revalidate、fetchCache、dynamicParams はエラーになり、Edge ランタイムはサポートされません。revalidate = N は use cache 内で cacheLife(最も近いプロファイルまたはカスタムプロファイル)に置き換えます。 force-dynamic と fetchCache は不要です。fetch の next: { revalidate, tags } は、use cache 関数内の cacheLife/cacheTag に置き換えます。リクエストデータは Suspense の下に置きます。未知のパラメーターに対する dynamicParams = false は notFound() に置き換えます。
  • 既存の fetch と unstable_cache のキャッシュも別のレイヤーとして引き続き機能するため、すべての読み取りを機械的に書き換えないでください。デプロイやインスタンスをまたいでキャッシュが保持されるかどうかは、ストレージが保持されているか、共有されているかによって異なります。セルフホストのインスタンスは、デフォルトではキャッシュを共有しません。use cache はデフォルトでメモリ内に保存されます。永続化可能なハンドラーを使っても、ビルド/デプロイ ID が変わるとエントリーを再利用できません。
  • generateStaticParams は少なくとも 1 つのパラメーターを返す必要があります。[] はビルドに失敗し、エクスポートを削除すると、リクエストのたびにルートがレンダリングされます。
  • revalidate: 0、または 5 分未満の expire を設定すると、プリレンダリング済みの出力ではなく、リクエスト時に解決される領域になります。プリセットのうち該当するのは seconds だけです。16.3 では stale のしきい値が追加されました(30 秒未満ではプリレンダリングされず、5 分未満では App Shell に含まれません)。
  • プリレンダリング中に同期的な new Date()、Math.random()、crypto.randomUUID() を呼び出すとエラーになります。use cache 内に取り込むか、Suspense の下で await io()(16.3 以降は next/cache)または await connection()(next/server)を使って後回しにしてください。
  • use cache: private は 16.2 まで実験的機能でした。リクエスト時に実行され、静的シェルには含まれず、カスタムハンドラーは受け付けません。
  • export const instant(16.3。16.2 では unstable_instant)を使うと、セグメントでインスタントナビゲーションの検証が有効になります。16.3 以降、すべての Page/Default セグメントは、デフォルトのレベルで next dev のみ(開発オーバーレイ)により暗黙的に検証されます。ビルド時の検証には、experimental.instantInsights.validationLevel を experimental-error または experimental-manual-error に設定する必要があります(16.2 の unstable_instant もビルド時に検証されていました)。ナビゲーションのテストには、@next/playwright の instant() を使います。instant = false にするとブロッキングルートを許可しますが、同期 I/O のエラーは解消されません。

アプリ全体への導入やインスタントナビゲーションの作業向けに、Next.js は vercel/next.js/skills でワークフロー用スキル(next-cache-components-adoption、next-cache-components-optimizer)を公開しています。同梱の移行ガイドにも同じ手順が記載されています。

1 つのルートの構成

cacheComponents: true を指定した next 16.3.8 で確認した app/posts/page.tsx(tsc --noEmit、next build、next start でのアクション)。@/lib/* はプロジェクトのコードです。

import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag, updateTag } from 'next/cache'
import { insertPost, listPosts } from '@/lib/posts' // project code
import { requireEditor } from '@/lib/auth' // throws unless the session may edit

async function getPosts() {
  'use cache' // shared entry: no cookies/headers/searchParams inside
  cacheLife('hours')
  cacheTag('posts')
  return listPosts()
}

async function Theme() {
  const theme = (await cookies()).get('theme')?.value // request-time, outside the cache
  return <p>Theme: {theme ?? 'system'}</p>
}

async function addPost(formData: FormData) {
  'use server'
  await requireEditor()
  const title = String(formData.get('title') ?? '').trim()
  if (!title) return
  await insertPost(title)
  updateTag('posts') // Server Actions only; the next read waits for fresh data
}

export default async function Page() {
  const posts = await getPosts() // cached, so it can be part of the static shell
  return (
    <>
      <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
      <Suspense fallback={<p>Theme: …</p>}><Theme /></Suspense>
      <form action={addPost}><input name="title" /><button>Add</button></form>
    </>
  )
}

境界を決める

  • プロダクトの鮮度要件とデータポリシーが許す場合は、再利用可能な結果をキャッシュします。 静的コンテンツを静的にするだけなら、ディレクティブは不要です。
  • 通常の use cache スコープ内では、Cookie、ヘッダー、searchParams を読み取ったり、connection() を呼び出したりできません。ヘルパー経由で間接的に行う場合も同様です。これらはスコープの外で読み取り、認可済みのシリアライズ可能な値を渡します。 出力がアカウント/テナント ID に依存する場合、それらの ID をキャッシュキーに含める必要があります。 動的にレンダリングされるルートでは、next-request-in-use-cache エラーが next build を通過し、next start でのみ発生することがあります。
  • キャッシュされない I/O とリクエスト専用のコンテンツは、適切な Suspense 境界の下に置きます。Suspense はフォールバック UI を提供しますが、それ自体で同期処理を動的にするわけではありません。
  • プライベート/リモートの各バリアントは、インストール済みのドキュメントとデプロイ時のハンドラーに照らして評価してください。プライベートキャッシュはコンプライアンスを保証するものではありません。リモートキャッシュは整合性プロトコルではありません。

鮮度と無効化を定義する

許容できる実際の古さに基づいて有効期間を選びます。名前付きプロファイルはプロジェクト側で上書きできるため、期間を決めつける前に設定を確認してください。

無効化を同じエンティティの複数のルートに反映させる必要がある場合はタグを、ルート固有の出力にはパスの無効化を、要件を満たす場合は有効期限を使用します。変更を加える前に認証、認可、検証を行ってください。影響を受けたキャッシュデータのみを無効化します。

  • updateTag:即時に期限切れにし、書き込み後の読み取りを実現します。Server Actions 専用です。
  • revalidateTag(tag, 'max'):次回のアクセス時に stale-while-revalidate を行います。
  • revalidateTag(tag, { expire: 0 }):Webhook/Route Handler で即時の期限切れが必要な場合に使用します。引数が1つの形式は非推奨です。

これらのタグ API はタグ付けされた fetch データにも適用できます。これらが利用可能だからといって、Cache Components が有効になっているとは限りません。

問題に応じて参照する資料

  • API の境界:キー、シリアライズ、有効期間、ハンドラー、ルート API。
  • 構成パターン:テナント分離、ネストされたキャッシュ、パススルー、動的パラメーター。
  • トラブルシューティング:ブロッキング、古さ、不整合のある出力を診断します。

本番ビルドを実行してから、next start のもとで、キャッシュがない状態での直接ナビゲーション、クライアントナビゲーション、該当する変更、その後の読み取りを試してください。該当する場合は、テナント分離と複数インスタンス間の無効化も確認します。ビルドが成功しても、こうした動作が保証されるわけではありません。

コメント