增量静态再生 (ISR)
Next.js 允许您在站点构建完成后创建或更新静态页面。增量静态再生 (ISR) 使您能够基于每个页面使用静态生成,而无需重新构建整个站点。通过 ISR,您可以在扩展到数百万页面的同时保留静态化的优势。
须知:当前
edge
运行时 与 ISR 不兼容,但您可以通过手动设置cache-control
标头来利用stale-while-revalidate
策略。
要使用 ISR,请在 getStaticProps
中添加 revalidate
属性:
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
// 此函数在构建时于服务端调用
// 如果启用了重新验证且有新请求进入,可能会在无服务器函数上再次调用
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js 将尝试重新生成页面:
// - 当有请求进入时
// - 最多每 10 秒一次
revalidate: 10, // 单位为秒
}
}
// 此函数在构建时于服务端调用
// 如果路径尚未生成,可能会在无服务器函数上再次调用
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// 根据文章获取需要预渲染的路径
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// 我们将在构建时仅预渲染这些路径
// { fallback: 'blocking' } 会在路径不存在时按需服务端渲染页面
return { paths, fallback: 'blocking' }
}
export default Blog
当向构建时预渲染的页面发出请求时,最初会显示缓存页面:
- 在初始请求后的 10 秒内,对该页面的所有后续请求也将从缓存中即时提供
- 10 秒窗口期过后,下一个请求仍会显示缓存的(过时)页面
- Next.js 会在后台触发页面重新生成
- 页面成功生成后,Next.js 将使缓存失效并显示更新后的页面。如果后台重新生成失败,旧页面将保持不变
当向尚未生成的路径发出请求时,Next.js 会在首次请求时服务端渲染该页面。后续请求将从缓存中提供静态文件。Vercel 上的 ISR 会全局持久化缓存并处理回滚。
须知:检查您的上游数据提供商是否默认启用了缓存。您可能需要禁用缓存(例如设置
useCdn: false
),否则重新验证将无法获取新数据来更新 ISR 缓存。当端点返回Cache-Control
标头时,CDN 可能会对请求的端点进行缓存。
按需重新验证
如果您将 revalidate
时间设置为 60
,则所有访问者在一分钟内将看到相同的生成版本。使缓存失效的唯一方式是在一分钟后有人访问该页面。
从 v12.2.0
开始,Next.js 支持按需增量静态再生,可以手动清除特定页面的 Next.js 缓存。这在以下场景中更容易更新您的站点:
- 无头 CMS 中的内容被创建或更新
- 电子商务元数据发生变化(价格、描述、类别、评论等)
在 getStaticProps
中,您不需要指定 revalidate
来使用按需重新验证。如果省略 revalidate
,Next.js 将使用默认值 false
(不重新验证),仅在调用 revalidate()
时按需重新验证页面。
须知:对于按需 ISR 请求,中间件 不会执行。相反,应在您想要重新验证的 确切 路径上调用
revalidate()
。例如,如果您有pages/blog/[slug].js
和从/post-1
重写到/blog/post-1
的规则,则需要调用res.revalidate('/blog/post-1')
。
使用按需重新验证
首先,创建一个只有您的 Next.js 应用知道的密钥令牌。此密钥将用于防止未经授权访问重新验证 API 路由。您可以通过以下 URL 结构访问该路由(手动或通过 webhook):
https://<your-site.com>/api/revalidate?secret=<token>
接下来,将密钥作为环境变量添加到您的应用中。最后,创建重新验证 API 路由:
export default async function handler(req, res) {
// 检查密钥以确认这是有效请求
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: '无效令牌' })
}
try {
// 这应该是实际路径而非重写路径
// 例如对于 "/blog/[slug]",这应该是 "/blog/post-1"
await res.revalidate('/path-to-revalidate')
return res.json({ revalidated: true })
} catch (err) {
// 如果出现错误,Next.js 将继续
// 显示最后成功生成的页面
return res.status(500).send('重新验证时出错')
}
}
查看我们的演示以了解按需重新验证的实际效果并提供反馈。
开发期间测试按需 ISR
在使用 next dev
本地运行时,getStaticProps
会在每个请求上调用。要验证您的按需 ISR 配置是否正确,您需要创建生产构建并启动生产服务器:
$ next build
$ next start
然后,您可以确认静态页面已成功重新验证。
错误处理和重新验证
如果在处理后台重新生成时 getStaticProps
内部出现错误,或者您手动抛出错误,则会继续显示最后成功生成的页面。在下一个后续请求中,Next.js 将重试调用 getStaticProps
。
export async function getStaticProps() {
// 如果此请求抛出未捕获的错误,Next.js 将
// 不会使当前显示的页面失效,并在
// 下一个请求时重试 getStaticProps
const res = await fetch('https://.../posts')
const posts = await res.json()
if (!res.ok) {
// 如果是服务器错误,您可能希望
// 抛出错误而不是返回,这样缓存将不会更新
// 直到下一个成功的请求
throw new Error(`获取文章失败,收到状态码 ${res.status}`)
}
// 如果请求成功,返回文章
// 并每 10 秒重新验证一次
return {
props: {
posts,
},
revalidate: 10,
}
}
自托管 ISR
增量静态再生 (ISR) 在自托管 Next.js 站点上开箱即用,只需使用 next start
即可。
了解更多关于自托管 Next.js 的信息。
版本历史
版本 | 变更 |
---|---|
v14.1.0 | 自定义 cacheHandler 稳定化 |
v12.2.0 | 按需 ISR 稳定化 |
v12.1.0 | 添加按需 ISR(测试版) |
v12.0.0 | 添加机器人感知的 ISR 回退 |
v9.5.0 | 添加基础路径支持 |