Incremental Static Regeneration (ISR)
Incremental Static Regeneration (ISR) により、次のことが可能になります:
- サイト全体を再構築することなく静的コンテンツを更新する
- ほとんどのリクエストに対して事前レンダリングされた静的ページを提供することでサーバー負荷を軽減する
- 適切な
cache-control
ヘッダーがページに自動的に追加されることを保証する - 長い
next build
時間をかけずに大量のコンテンツページを処理する
以下は最小限の例です:
- TypeScript
- JavaScript
interface Post {
id: string
title: string
content: string
}
// Next.jsはリクエストが来たときに、最大で60秒ごとにキャッシュを無効化します。
export const revalidate = 60
// ビルド時に `generateStaticParams` からのパラメータのみを事前レンダリングします。
// 生成されていないパスに対してリクエストが来た場合、Next.jsはオンデマンドでページをサーバーレンダリングします。
export const dynamicParams = true // または false にして、未知のパスで404を返す
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.jsはリクエストが来たときに、最大で60秒ごとにキャッシュを無効化します。
export const revalidate = 60
// ビルド時に `generateStaticParams` からのパラメータのみを事前レンダリングします。
// 生成されていないパスに対してリクエストが来た場合、Next.jsはオンデマンドでページをサーバーレンダリングします。
export const dynamicParams = true // または false にして、未知のパスで404を返す
export async function generateStaticParams() {
const posts = 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 }) {
const { id } = await params
const 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
の間に、すべての既知のブログ投稿が生成されます(この例では25件あります)- これらのページへのすべてのリクエスト(例:
/blog/1
)はキャッシュされ、即座に応答します - 60秒が経過した後、次のリクエストはキャッシュされた(古い)ページを表示します
- キャッシュが無効化され、ページの新しいバージョンがバックグラウンドで生成され始めます
- 正常に生成されると、Next.jsは更新されたページを表示し、キャッシュします
/blog/26
がリクエストされた場合、Next.jsはこのページをオンデマンドで生成し、キャッシュします
参考
Route segment config
Functions
例
時間ベースの再検証
これは /blog
でブログ投稿のリストを取得して表示します。1時間後、このページのキャッシュは次回のページ訪問時に無効化されます。その後、バックグラウンドで最新のブログ投稿を含むページの新しいバージョンが生成されます。
- TypeScript
- JavaScript
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>
)
}
export const revalidate = 3600 // 1時間ごとに無効化
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<main>
<h1>Blog Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
再検証時間を長く設定することをお勧めします。例えば、1秒ではなく1時間です。より精密な再検証が必要な場合は、オンデマンド再検証を検討してください。リアルタイムデータが必要な場合は、dynamic renderingへの切り替えを検討してください。
revalidatePath
を使用したオンデマンド再検証
より精密な再検証方法として、revalidatePath
関数を使用してオンデマンドでページを無効化します。
例えば、新しい投稿を追加した後にこのServer Actionが呼び出されます。Server Componentでデータを取得する方法に関係なく、fetch
を使用するかデータベースに接続するかにかかわらず、これによりルート全体のキャッシュがクリアされ、Server Componentが新しいデータを取得できるようになります。
- TypeScript
- JavaScript
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// キャッシュ内の /posts ルートを無効化
revalidatePath('/posts')
}
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// キャッシュ内の /posts ルートを無効化
revalidatePath('/posts')
}
デモを見るおよびソースコードを探索する。
revalidateTag
を使用したオンデマンド再検証
ほとんどのユースケースでは、全体のパスを再検証することをお勧めします。より細かい制御が必要な場合は、revalidateTag
関数を使用できます。例えば、個々の fetch
呼び出しにタグを付けることができます:
- TypeScript
- JavaScript
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
const posts = await data.json()
// ...
}
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
を使用できます:
- TypeScript
- JavaScript
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()
// ...
}
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
を使用できます:
- TypeScript
- JavaScript
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
// キャッシュ内の 'posts' タグが付けられたすべてのデータを無効化
revalidateTag('posts')
}
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
// キャッシュ内の 'posts' タグが付けられたすべてのデータを無効化
revalidateTag('posts')
}
キャッチされない例外の処理
データを再検証しようとした際にエラーが発生した場合、最後に正常に生成されたデータがキャッシュから引き続き提供されます。次のリクエスト時に、Next.jsはデータの再検証を試みます。エラー処理について詳しく学ぶ。
キャッシュの場所をカスタマイズする
ページのキャッシュと再検証(Incremental Static Regenerationを使用)は、同じ共有キャッシュを使用します。Vercelにデプロイする場合、ISRキャッシュは自動的に耐久性のあるストレージに保存されます。
セルフホスティングの場合、ISRキャッシュはNext.jsサーバーのファイルシステム(ディスク上)に保存されます。PagesとApp Routerの両方を使用してセルフホスティングする場合に自動的に機能します。
キャッシュされたページとデータを耐久性のあるストレージに保存したり、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
です。
バージョン履歴
Version | Changes |
---|---|
v14.1.0 | カスタム cacheHandler が安定版になりました。 |
v13.0.0 | App Router が導入されました。 |
v12.2.0 | Pages Router: オンデマンドISRが安定版になりました |
v12.0.0 | Pages Router: Bot-aware ISR fallback が追加されました。 |
v9.5.0 | Pages Router: 安定版ISRが導入されました。 |