Next.js のキャッシュ。いつ描かれるのか
Next.js のキャッシュは、そのページを事前に1枚作って全員に配るか、アクセスのたびに描くかの二択で、どちらになるかは何を使ったかで決まります。
聞くべきことは1つだけ
キャッシュの話は複雑に見えますが、ページ単位で見れば問いは1つです。
このページは、いつ描かれるのか。
答えは2つしかありません。ビルドのときに1枚だけ描いて全員に配るか、アクセスのたびに描くか。ページの中で読む process.env も、描いたときに読まれます(環境変数はいつ読まれるのか)。そしてどちらになるかは、自分で選ぶのではなく、何を使ったかで決まります。
ビルドの出力が答えを持っている
覚える必要はありません。ビルドすると、ページごとにどちらになったかが出ます。実機の出力です。
Route (app) Revalidate Expire
┌ ○ / ← 事前に1枚
├ ƒ /cookie ← 毎回描く
├ ○ /revalidated 1m 1y ← 事前に1枚。1分で作り直す
└ ƒ /search ← 毎回描く
| 記号 | 意味 | いつ描かれるか |
|---|---|---|
| ○ | Static | ビルドのとき1回 |
| ● | SSG | ビルドのとき、パスの数だけ |
| ƒ | Dynamic | アクセスのたび |
設計どおりかを確かめる場所は、コードではなくここです。
何を使うと、毎回描く側へ倒れるか
/cookie と /search が動的になったのは、次のものを使ったからです。
| 使ったもの | なぜ倒れるか |
|---|---|
| cookies() | 人によって違う |
| headers() | リクエストごとに違う |
| searchParams | URL のたびに違う |
実機で確かめました。Cookie を1行読むだけのページは動的になり、成果物を検索してもその画面の文字は1件も見つかりません。事前に描かれていないからです。
「毎回描く」と「古くなったら作り直す」は別のもの
ここが混ざりやすいところです。
export const dynamic = 'force-dynamic'アクセスのたびにサーバーが働く。全員が待つ。
export const revalidate = 60配るのは出来あがったもの。作り直しは裏で起きる。
revalidate を書いたページは、事前に描かれたままです。実機の一覧でも ○ のまま Revalidate 1m と出ます。配るときは出来あがったものをそのまま返すので、誰も待ちません。
秒数は「古い内容が出ても困らない時間」で決めます。在庫や価格のように間違うと困るものは、時間で切るのではなく、更新したときに作り直す形にします(更新を反映させる最小構成)。データにタグを付けているなら、タグ単位で捨てる revalidateTag もあります。Next.js 16 で呼び方が変わったので注意してください(revalidateTag の第2引数と updateTag)。
force-static は、黙って空を焼き付ける
これがいちばん危険です。実機で確かめました。
export const dynamic = 'force-static'
import { cookies } from 'next/headers'
export default async function Page() {
const c = await cookies()
const name = c.get('user')?.value
return <p>{name ? name + ' さん' : 'ログインしていません'}</p>
}
エラーも警告も出ません。ビルドは通り、出来あがった HTML はこうなります。
<p>ログインしていません</p>
この1枚がログイン済みの人にも配られます。
動的なパスは、一覧を渡したぶんだけ事前に描かれる
app/blog/[slug]/page.tsx のようなパスは、そのままでは毎回描く側です。どの slug が来るか分からないからです。
generateStaticParams で一覧を渡すと、その数だけ事前に描かれます。実機の出力です。
├ /blog/[slug]
│ ├ ● /blog/a
│ └ ● /blog/b
└ ƒ /news/[id] ← 一覧を渡していないほう
記事のように数が決まっているものは渡す、検索結果のように決まらないものは渡さない、が目安です。
確かめ方
pnpm buildする- ルートの一覧で、意図した記号になっているか見る
- 違っていたら、そのページが何を使っているかを探す
- Build一覧で ƒ になっている
事前に描かれていない。ここが出発点。
- Servercookies / headers / searchParams を探す
ページ本体だけでなく、呼んでいる関数の中まで見る。
- Buildその部分を Suspense で囲む
外側は事前に描かれたまま残せる。
「キャッシュが効かない」の正体は、たいてい自分で動的に倒していることです。設定を足して直すより、何を使っているかを探すほうが早く着きます。
出典
- nextjs.org/docs/app/getting-started/caching
- nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
- nextjs.org/docs/app/guides/incremental-static-regeneration
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。