跳到内容

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

示例

增量静态再生 (ISR) 允许您

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

这是一个最小示例

app/blog/[id]/page.tsx
interface Post {
  id: string
  title: string
  content: string
}
 
// Next.js will invalidate the cache when a
// request comes in, at most once every 60 seconds.
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>
  )
}

此示例的工作方式如下

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

参考

路由段配置

函数

示例

基于时间重验证

这会在 /blog 上获取并显示博客文章列表。一个小时过后,下一个访问者仍会立即收到页面缓存(过期)版本以获得快速响应。同时,Next.js 会在后台触发新版本的再生。一旦新版本成功生成,它会替换缓存版本,后续访问者将收到更新后的内容。

app/blog/page.tsx
interface Post {
  id: string
  title: string
  content: string
}
 
export const revalidate = 3600 // invalidate every hour
 
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 函数按需使缓存页面失效。

例如,此服务器操作会在添加新帖子后调用。无论您在服务器组件中如何检索数据,无论是使用 fetch 还是连接到数据库,这都将使整个路由的缓存失效。对该路由的下一个请求将触发重新生成并提供最新数据,然后该数据将缓存以供后续请求使用。

注意: revalidatePath 使缓存条目失效,但重新生成发生在下一个请求上。如果您希望立即主动重新生成缓存条目,而不是等待下一个请求,您可以使用 Pages 路由 res.revalidate 方法。我们正在努力添加新方法,以提供 App Router 的主动重新生成功能。

app/actions.ts
'use server'
 
import { revalidatePath } from 'next/cache'
 
export async function createPost() {
  // Invalidate the cache for the /posts route
  revalidatePath('/posts')
}

查看演示探索源代码

使用 revalidateTag 按需重验证

对于大多数用例,首选重新验证整个路径。如果您需要更细粒度的控制,可以使用 revalidateTag 函数。例如,您可以标记单个 fetch 调用

app/blog/page.tsx
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

app/blog/page.tsx
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()
  // ...
}

然后您可以在服务器操作路由处理程序中使用 revalidateTag

app/actions.ts
'use server'
 
import { revalidateTag } from 'next/cache'
 
export async function createPost() {
  // Invalidate all data tagged with 'posts'
  revalidateTag('posts')
}

处理未捕获的异常

如果在尝试重新验证数据时发生错误,则上次成功生成的数据将继续从缓存中提供。在下一个请求中,Next.js 将重试重新验证数据。了解更多关于错误处理的信息

自定义缓存位置

如果您希望将缓存页面和数据持久化到持久存储,或在 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。
  • 如果静态渲染路由中有多个 fetch 请求,并且每个请求都有不同的 revalidate 频率,则将使用最低时间作为 ISR。但是,这些重新验证频率仍将由数据缓存遵循。
  • 如果路由上使用的任何 fetch 请求的 revalidate 时间为 0 或显式 no-store,则该路由将动态渲染
  • 代理不会为按需 ISR 请求执行,这意味着代理中的任何路径重写或逻辑都不会应用。请确保您正在重新验证精确的路径。例如,/post/1 而不是重写的 /post-1

平台支持

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

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

版本历史

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