增量静态再生 (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 路由:

pages/api/revalidate.js
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添加基础路径支持

On this page