How to implement Incremental Static Regeneration (ISR)
增量静态再生(ISR)使你能够:
- 更新静态内容而无需重新构建整个站点
- 通过为大多数请求提供预渲染的静态页面来减少服务器负载
- 确保自动为页面添加正确的
cache-control头 - 处理大量内容页面而无需较长的
next build时间
这是一个最小示例:
interface Post {
id: string
title: string
content: string
}
// 当请求到来时,Next.js 将使缓存失效,
// 最多每 60 秒一次。
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。
参考
路由段配置
函数
示例
基于时间的重新验证
这会在 /blog 上获取并显示博客文章列表。一小时后,下一个访问者仍会立即收到页面的缓存(过时)版本以获得快速响应。同时,Next.js 在后台触发新版本的再生。成功生成新版本后,它将替换缓存的版本,后续访问者将收到更新的内容。
interface Post {
id: string
title: string
content: string
}
export const revalidate = 3600 // 每小时使缓存失效一次
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。无论你如何在 Server Component 中检索数据,无论是使用 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。
- 如果在静态渲染的路由中有多个
fetch请求,每个请求具有不同的revalidate频率,则 ISR 将使用最短时间。但是,这些重新验证频率仍将受 Data Cache 的遵守。 - 如果路由上使用的任何
fetch请求的revalidate时间为0,或显式的no-store,则该路由将被动态渲染。 - 代理不会对按需 ISR 请求执行,这意味着代理中的任何路径重写或逻辑都不会被应用。确保你正在重新验证确切的路径。例如,
/post/1而不是重写的/post-1。
平台支持
| 部署选项 | 支持情况 |
|---|---|
| Node.js 服务器 | 是 |
| Docker 容器 | 是 |
| 静态导出 | 否 |
| 适配器 | 取决于平台 |
了解如何在自托管 Next.js 时配置 ISR。
版本历史
| 版本 | 变更 |
|---|---|
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。 |