Incremental Static Regeneration(ISR)の実装方法
Incremental Static Regeneration(ISR)により、以下が実現できます:
- サイト全体をリビルドせずに静的コンテンツを更新
- 大多数のリクエストに対して事前レンダリングされた静的ページを提供することでサーバー負荷を削減
- 適切な
cache-controlヘッダーがページに自動的に追加される next buildの時間を増やさずに大量のコンテンツページを処理
最小限の例を以下に示します:
interface Post {
id: string
title: string
content: string
}
// Next.jsは最大60秒ごとに1回、
// リクエストが来たときにキャッシュを無効化します。
export const revalidate = 60
export async function generateStaticParams() {
const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return posts.map((post) => ({
id: String(post.id),
}))
}
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
(res) => res.json()
)
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
)
}この例がどのように機能するかは以下の通りです:
next build中に、すべての既知のブログ記事が生成されます- これらのページへのすべてのリクエスト(例:
/blog/1)がキャッシュされ、即座に返されます - 60秒経過後、次のリクエストはキャッシュされた(古い)ページを返します
- キャッシュが無効化され、ページの新しいバージョンがバックグラウンドで生成され始めます
- 正常に生成されたら、次のリクエストは更新されたページを受け取り、後続のリクエスト用にキャッシュされます
/blog/26がリクエストされ、それが存在する場合、ページはオンデマンドで生成されます。この動作は別のdynamicParams値を使用することで変更できます。ただし、記事が存在しない場合は404が返されます。
リファレンス
Route segment config
関数
例
時間ベースの再検証
これは/blogのブログ記事のリストを取得して表示します。1時間経過後、次の訪問者はすぐに高速な応答を得るためにキャッシュされた(古い)バージョンのページを受け取ります。同時に、Next.jsはバックグラウンドで新しいバージョンの再生成をトリガーします。新しいバージョンが正常に生成されたら、キャッシュされたバージョンを置き換え、後続の訪問者は更新されたコンテンツを受け取ります。
interface Post {
id: string
title: string
content: string
}
export const revalidate = 3600 // 1時間ごとに無効化
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts: Post[] = await data.json()
return (
<main>
<h1>Blog Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}高い再検証時間を設定することをお勧めします。例えば、1秒ではなく1時間にします。より高い精度が必要な場合は、オンデマンド再検証の使用を検討してください。リアルタイムデータが必要な場合は、動的レンダリングへの切り替えを検討してください。
revalidatePathを使用したオンデマンド再検証
より正確な再検証方法のために、revalidatePath関数を使用してキャッシュされたページをオンデマンドで無効化します。
例えば、このServer Actionは新しい記事を追加した後に呼び出されます。fetchを使用してデータを取得するか、データベースに接続するかに関わらず、ルート全体のキャッシュを無効化します。その後のそのルートへのリクエストは再生成をトリガーし、新鮮なデータを提供します。新鮮なデータはその後の後続リクエストのためにキャッシュされます。
補足:
revalidatePathはキャッシュエントリを無効化しますが、再生成は次のリクエストで発生します。次のリクエストを待つ代わりに、すぐにキャッシュエントリを積極的に再生成したい場合は、Pages routerのres.revalidateメソッドを使用できます。App Routerに積極的な再生成機能を提供するための新しいメソッドの追加に取り組んでいます。
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// /postsルートのキャッシュを無効化
revalidatePath('/posts')
}revalidateTagを使用したオンデマンド再検証
ほとんどのユースケースでは、パス全体の再検証が推奨されます。より細粒度な制御が必要な場合は、revalidateTag関数を使用できます。例えば、個別のfetch呼び出しにタグを付けることができます:
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
const posts = await data.json()
// ...
}ORMを使用しているか、データベースに接続している場合は、unstable_cacheを使用できます:
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
const getCachedPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)
export default async function Page() {
const posts = getCachedPosts()
// ...
}その後、Server ActionsまたはRoute HandlerでrevalidateTagを使用できます:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
// 「posts」タグ付きのすべてのデータを無効化
revalidateTag('posts')
}キャッチされていない例外の処理
データを再検証する際にエラーがスローされた場合、最後に正常に生成されたデータはキャッシュから提供され続けます。その後のリクエストで、Next.jsはデータの再検証を再試行します。エラーハンドリングの詳細を学習してください。
キャッシュの場所のカスタマイズ
キャッシュされたページとデータを永続的なストレージに保持したい場合、または複数のコンテナーまたはNext.jsアプリケーションのインスタンス間でキャッシュを共有したい場合は、Next.jsキャッシュの場所を設定できます。詳細を学習してください。
トラブルシューティング
ローカル開発でキャッシュされたデータをデバッグ
fetch APIを使用している場合は、どのリクエストがキャッシュされているか、されていないかを理解するために追加のロギングを追加できます。loggingオプションの詳細を学習してください。
module.exports = {
logging: {
fetches: {
fullUrl: true,
},
},
}本番環境での正しい動作を確認
ページがキャッシュされ、本番環境で正しく再検証されていることを確認するために、next buildを実行してからnext startを実行して本番Next.jsサーバーを実行することで、ローカルでテストできます。
これにより、ISR動作を本番環境で動作する方法でテストすることができます。さらにデバッグするために、以下の環境変数を.envファイルに追加します:
NEXT_PRIVATE_DEBUG_CACHE=1これにより、Next.jsサーバーコンソールはISRキャッシュのヒットとミスをログに出力します。出力を検査して、next build中に生成されたページと、パスがオンデマンドでアクセスされるときにページがどのように更新されるかを確認できます。
注意事項
- ISRはNode.jsランタイム(デフォルト)を使用する場合のみサポートされます。
- ISRはStatic Exportを作成する場合はサポートされません。
- 静的にレンダリングされたルートに複数の
fetchリクエストがあり、それぞれが異なるrevalidate頻度を持つ場合、最も低い時間がISRに使用されます。ただし、これらの再検証頻度は引き続きData Cacheによって尊重されます。 - ルートで使用される
fetchリクエストのいずれかにrevalidate時間が0の場合、または明示的なno-storeがある場合、ルートは動的にレンダリングされます。 - プロキシはオンデマンドISRリクエストに対して実行されません。つまり、プロキシ内のパス書き直しまたはロジックは適用されません。正確なパスを再検証していることを確認します。例えば、書き直された
/post-1ではなく/post/1です。
プラットフォームサポート
| デプロイメントオプション | サポート状況 |
|---|---|
| Node.jsサーバー | あり |
| Dockerコンテナ | あり |
| Static export | なし |
| Adapters | プラットフォーム依存 |
Next.jsを自己ホストする場合のISRの設定方法を学習してください。
バージョン履歴
| バージョン | 変更内容 |
|---|---|
v14.1.0 | カスタムcacheHandlerが安定化。 |
v13.0.0 | App Routerが導入。 |
v12.2.0 | Pages Router:On-Demand ISRが安定化。 |
v12.0.0 | Pages Router:Bot-aware ISR fallbackを追加。 |
v9.5.0 | Pages Router:Stable ISRが導入。 |