ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
この記事の要点
- バージョン 16 のキャッシュは Cache Components に整理された。
next.config.tsにcacheComponents: trueを書いて有効化する。 - キャッシュしたい関数やコンポーネントの本体先頭に
'use cache'と書く。オプションではなくコード側で宣言する。 - 引数とスコープ外から参照している値がキャッシュキーになる。引数が違えば別のエントリになる。
- キャッシュしないデータや
cookies()などはSuspenseで包む。これが「静的な殻+動的な中身」を両立させる形になる。 Math.random()やDate.now()は明示的な扱いが必要。リクエストごとに変えるならconnection()を呼ぶ。
Next.js のキャッシュは歴史的に何度も設計が変わってきた領域で、日本語の解説が最も食い違いやすい部分でもあります。バージョン 13 では fetch が既定でキャッシュされ、export const revalidate のようなルート単位の設定が主役でした。バージョン 16 では考え方が変わり、キャッシュするものをコード側で宣言する方式になっています。この記事ではその新しいモデルを扱います。
1有効化する
Cache Components は設定で有効にします。
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
これは名前替えではありません。有効にすると、キャッシュも Suspense も無いままリクエスト時のデータへアクセスしている箇所がビルドエラーとして表面化します。既存プロジェクトに後から入れる場合は、その修正込みで計画してください。
2use cache の 2 つの粒度
'use cache' は関数本体の先頭に書きます。適用の仕方は 2 段階あります。
データ単位。取得や計算を行う関数をキャッシュします。同じデータを複数のコンポーネントで使う場合や、UI と切り離して寿命を管理したい場合に向きます。
// app/lib/data.ts
import { cacheLife } from 'next/cache'
export async function getUsers() {
'use cache'
cacheLife('hours')
return db.query('SELECT * FROM users')
}
UI 単位。コンポーネントやページ全体をキャッシュします。描画結果ごと保存されるため、取得と整形の両方を省けます。
// app/page.tsx
import { cacheLife } from 'next/cache'
export default async function Page() {
'use cache'
cacheLife('hours')
const users = await db.query('SELECT * FROM users')
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
ファイルの先頭に書けば、そのファイルの全エクスポートがキャッシュ対象になります。また、cacheLife を必ず添えることが推奨されています。省略すると既定のプロファイルが適用されますが、意図が読めなくなるためです。
3キャッシュキーは引数で決まる
キャッシュの区別は引数と、関数が外側のスコープから参照している値で行われます。
async function getProduct(id: string) {
'use cache'
cacheLife('hours')
return db.products.find(id)
}
// getProduct('a') と getProduct('b') は別々に保存される
したがって、ユーザーごとに変わる値を引数として渡せば、ユーザーごとのキャッシュを作れます。逆に言うと、キーになりうる値を引数に取らずに関数の中で直接読んでしまうと、全員が同じ結果を共有してしまいます。
4キャッシュしないものは Suspense で包む
毎回新鮮でなければならないデータには 'use cache' を付けません。代わりに Suspense で包みます。
import { Suspense } from 'react'
async function LatestPosts() {
const data = await fetch('https://api.example.com/posts')
const posts = await data.json()
return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
}
export default function Page() {
return (
<>
<h1>ブログ</h1>
<Suspense fallback={<p>読み込み中…</p>}>
<LatestPosts />
</Suspense>
</>
)
}
cookies()、headers()、searchParams、params といったリクエスト依存の値も同じ扱いです。これらを Suspense なしで読むと、開発時のオーバーレイに「ルート全体をブロックしている」という指摘が出ます。
5リクエスト依存の値をキャッシュに渡す
「セッションごとに違うが、同じセッションなら使い回せる」データもあります。この場合は、リクエスト依存の値をいったん取り出してから、引数としてキャッシュ関数に渡します。
import { cookies } from 'next/headers'
import { Suspense } from 'react'
export default function Page() {
return (
<Suspense fallback={<div>読み込み中…</div>}>
<ProfileContent />
</Suspense>
)
}
// キャッシュしない側でリクエスト値を読む
async function ProfileContent() {
const session = (await cookies()).get('session')?.value
return <CachedContent sessionId={session} />
}
// 受け取った値がキャッシュキーになる
async function CachedContent({ sessionId }: { sessionId: string }) {
'use cache'
const data = await fetchUserData(sessionId)
return <div>{data}</div>
}
なお、既定のキャッシュはインスタンスごとのメモリ上に置かれます。サーバーレス環境のようにインスタンスが使い捨てられる構成では長持ちしません。永続的に共有したい場合は 'use cache: remote' を使い、外部ストアを設定します。逆に、ブラウザ側にだけ保持したい場合は 'use cache: private' という選択肢もあります。
6ランダム値と現在時刻
Math.random()、Date.now()、crypto.randomUUID() は実行のたびに違う値になるため、事前生成と相性が悪く、明示的な指定が必要です。
connection() を先に await してから生成し、そのコンポーネントを Suspense で包む。'use cache' を付ける。再検証されるまで同じ値が返る。import { connection } from 'next/server'
import { Suspense } from 'react'
async function UniqueContent() {
await connection() // ここから先はリクエスト時に実行される
const uuid = crypto.randomUUID()
return <p>リクエスト ID: {uuid}</p>
}
export default function Page() {
return (
<Suspense fallback={<p>読み込み中…</p>}>
<UniqueContent />
</Suspense>
)
}
一方、モジュールの読み込みや同期的なファイル読み取り、純粋な計算は毎回同じ結果になるため、そのまま事前生成に含まれます。設定ファイルのように「リクエストに依存せず変化もしない」ものは、コンポーネントの中ではなくモジュールのトップレベルで一度読むのが最も簡潔です。
7動的な URL と ISR
[slug] のような動的セグメントでは、generateStaticParams に列挙した URL がビルド時に生成されます。列挙していない URL に対しては、まず URL 非依存の共通部分(アプリシェル)が即座に返され、背後で具体的な内容が生成されて次の訪問者から使われます。これがバージョン 16 における ISR の姿です。
8つまずきやすいところ
- 古い記事の設定が効かない:
experimental.ppr、experimental.dynamicIO、experimental.useCacheはいずれも削除されました。cacheComponentsに置き換えます。 fetchが勝手にキャッシュされると思っている:バージョン 16 ではされません。キャッシュしたいなら'use cache'を明示します。- デプロイしたらキャッシュが消えた:キャッシュキーにビルド ID が含まれるため、新しいデプロイでは必ず作り直しになります。外部ストアを使っていても同じです。
- 個人情報が共有されてしまった:リクエスト依存の値をキャッシュ関数の外から読んでいないか確認します。キーに含まれない値を内部で読むのが事故のもとです。
キャッシュを置いたら、次はそれをいつ捨てるかという設計に進みます。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 Spring Frameworkのアノテーション一覧
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- PHPでのファイルアップロード方法|multipart/form-dataと$_FILES 2026-08-07 17:15:57
- PHP $_SERVERとは|DOCUMENT_ROOT・PHP_SELFなど主な要素 2026-08-07 17:15:57
- Scratchのアカウントの作り方|メールアドレスとユーザー名の登録手順 2026-08-07 17:15:57
- djangoにおけるMVCアプリケーション実装例 2026-08-07 17:15:57
- Bluetoothとは|仕組み・BLEとClassicの違い・用途 2026-08-07 17:15:57
- Spring Bootのテスト入門|@SpringBootTestとJUnitでの単体・結合テスト 2026-08-07 17:15:57
- Java とは?言語仕様・JVM・主要フレームワーク一覧 2026-08-07 17:15:56
- インストール方法(Windows版) 2026-08-07 17:15:56
- APIキー取得方法 2026-08-07 17:15:56
- 【PHPフレームワーク】Laravelの使い方 2026-08-07 17:15:56
- Scratch 作品の公開方法 2026-08-07 17:15:56
- Google OAuth 2.0 認証の実装方法 2026-08-07 17:15:56
- データベースとは?RDB / NoSQL / SQL の基礎 2026-08-07 17:15:56
- インストール(eclipseプラグイン) 2026-08-07 17:15:56
- Spring Frameworkの使い方 2026-08-07 17:15:56
コメントを削除してもよろしいでしょうか?