跳到内容

如何实现增量静态再生成 (ISR)

示例

增量静态再生成 (ISR) 使您能够

  • 更新静态内容而无需重新构建整个网站
  • 通过为大多数请求提供预渲染的静态页面来减少服务器负载
  • 确保页面自动添加正确的 cache-control
  • 处理大量内容页面而无需漫长的 next build 时间

这是一个最小示例

pages/blog/[id].tsx
import type { GetStaticPaths, GetStaticProps } from 'next'
 
interface Post {
  id: string
  title: string
  content: string
}
 
interface Props {
  post: Post
}
 
export const getStaticPaths: GetStaticPaths = async () => {
  const posts = await fetch('https://api.vercel.app/blog').then((res) =>
    res.json()
  )
  const paths = posts.map((post: Post) => ({
    params: { id: String(post.id) },
  }))
 
  return { paths, fallback: 'blocking' }
}
 
export const getStaticProps: GetStaticProps<Props> = async ({
  params,
}: {
  params: { id: string }
}) => {
  const post = await fetch(`https://api.vercel.app/blog/${params.id}`).then(
    (res) => res.json()
  )
 
  return {
    props: { post },
    // Next.js will invalidate the cache when a
    // request comes in, at most once every 60 seconds.
    revalidate: 60,
  }
}
 
export default function Page({ post }: Props) {
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}

此示例的工作原理如下

  1. next build 期间,所有已知的博客文章都会被生成
  2. 所有对这些页面的请求(例如 /blog/1)都会被缓存并立即返回
  3. 60 秒过后,下一个请求仍将返回缓存的(现已过时)页面
  4. 缓存失效,并在后台开始生成页面新版本
  5. 成功生成后,下一个请求将返回更新后的页面并将其缓存以供后续请求使用
  6. 如果请求 /blog/26 且它存在,页面将按需生成。此行为可以通过使用不同的fallback值来更改。但是,如果帖子不存在,则返回 404。

参考

函数

示例

使用 res.revalidate() 进行按需验证

为了更精确地重新验证,使用 res.revalidate 从 API 路由器按需生成一个新页面。

例如,可以在 /api/revalidate?secret=<token> 调用此 API 路由,以重新验证给定的博客文章。创建一个只有您的 Next.js 应用程序知道的秘密令牌。此秘密将用于防止未经授权访问重新验证 API 路由。

pages/api/revalidate.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // Check for secret to confirm this is a valid request
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }
 
  try {
    // This should be the actual path not a rewritten path
    // e.g. for "/posts/[id]" this should be "/posts/1"
    await res.revalidate('/posts/1')
    return res.json({ revalidated: true })
  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating')
  }
}

如果您正在使用按需重新验证,则无需在 getStaticProps 中指定 revalidate 时间。Next.js 将使用默认值 false(不重新验证),并且仅在调用 res.revalidate() 时按需重新验证页面。

处理未捕获的异常

如果在处理后台重新生成时 getStaticProps 内部出现错误,或者您手动抛出错误,则上次成功生成的页面将继续显示。在下一个后续请求中,Next.js 将重试调用 getStaticProps

pages/blog/[id].tsx
import type { GetStaticProps } from 'next'
 
interface Post {
  id: string
  title: string
  content: string
}
 
interface Props {
  post: Post
}
 
export const getStaticProps: GetStaticProps<Props> = async ({
  params,
}: {
  params: { id: string }
}) => {
  // If this request throws an uncaught error, Next.js will
  // not invalidate the currently shown page and
  // retry getStaticProps on the next request.
  const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
  const post: Post = await res.json()
 
  if (!res.ok) {
    // If there is a server error, you might want to
    // throw an error instead of returning so that the cache is not updated
    // until the next successful request.
    throw new Error(`Failed to fetch posts, received status ${res.status}`)
  }
 
  return {
    props: { post },
    // Next.js will invalidate the cache when a
    // request comes in, at most once every 60 seconds.
    revalidate: 60,
  }
}

自定义缓存位置

您可以配置 Next.js 缓存位置,如果您希望将缓存页面和数据持久化到持久存储中,或在 Next.js 应用程序的多个容器或实例之间共享缓存。 了解更多

故障排除

在本地开发中调试缓存数据

如果您正在使用 fetch API,您可以添加额外的日志以了解哪些请求被缓存或未缓存。了解有关 logging 选项的更多信息

next.config.js
module.exports = {
  logging: {
    fetches: {
      fullUrl: true,
    },
  },
}

验证正确的生产行为

要在生产环境中验证您的页面是否正确缓存和重新验证,您可以通过运行 next build,然后运行 next start 来运行生产 Next.js 服务器进行本地测试。

这将允许您测试 ISR 行为,就像它在生产环境中工作一样。为了进一步调试,请将以下环境变量添加到您的 .env 文件中

.env
NEXT_PRIVATE_DEBUG_CACHE=1

这将使 Next.js 服务器控制台记录 ISR 缓存命中和未命中。您可以检查输出以查看在 next build 期间生成了哪些页面,以及当按需访问路径时页面是如何更新的。

注意事项

  • ISR 仅在使用 Node.js 运行时(默认)时受支持。
  • 创建静态导出时不支持 ISR。
  • 代理不会为按需 ISR 请求执行,这意味着代理中的任何路径重写或逻辑都不会应用。请确保您重新验证的是确切路径。例如,/post/1 而不是重写的 /post-1

平台支持

部署选项支持
Node.js 服务器
Docker 容器
静态导出
适配器平台特定

了解如何在自托管 Next.js 时配置 ISR

版本历史

版本更改
v14.1.0自定义 cacheHandler 已稳定。
v13.0.0App 路由器已引入。
v12.2.0Pages 路由器:按需 ISR 已稳定
v12.0.0Pages 路由器:添加了机器人感知 ISR 回退
v9.5.0Pages 路由器:引入了稳定的 ISR