Menu

增量静态再生 (ISR)

示例

增量静态再生 (ISR) 使你能够:

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

下面是一个最小示例:

app/blog/[id]/page.tsx
TypeScript
interface Post {
  id: string
  title: string
  content: string
}
 
// Next.js 将在请求进来时使缓存失效,
// 最多每 60 秒一次。
export const revalidate = 60
 
// 我们只会在构建时预渲染来自 `generateStaticParams` 的参数。
// 如果收到一个尚未生成的路径的请求,
// Next.js 将按需服务器渲染该页面。
export const dynamicParams = true // 或 false,对未知路径返回 404
 
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: { id: string } }) {
  const post: Post = await fetch(
    `https://api.vercel.app/blog/${params.id}`
  ).then((res) => res.json())
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}

这个示例的工作原理如下:

  1. next build 期间,生成所有已知的博客文章 (本示例中有 25 篇)
  2. 对这些页面的所有请求 (例如 /blog/1) 都被缓存并立即响应
  3. 60 秒过后,下一个请求仍然会显示缓存的 (陈旧的) 页面
  4. 缓存失效,并在后台开始生成页面的新版本
  5. 一旦成功生成,Next.js 将显示并缓存更新后的页面
  6. 如果请求 /blog/26,Next.js 将按需生成并缓存这个页面

参考

路由段配置

函数

示例

基于时间的重新验证

这段代码在 /blog 路径下获取并显示博客文章列表。一小时后,在下次访问页面时该页面的缓存将失效。然后在后台,将使用最新的博客文章生成页面的新版本。

app/blog/page.tsx
TypeScript
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>博客文章</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  )
}

我们建议设置较长的重新验证时间。例如,设置为 1 小时而不是 1 秒。如果你需要更精确的控制,可以考虑使用按需重新验证。如果你需要实时数据,可以考虑切换到动态渲染

使用 revalidatePath 进行按需重新验证

对于更精确的重新验证方法,可以使用 revalidatePath 函数进行按需重新验证。

例如,这个服务器操作会在添加新文章后被调用。无论你在服务器组件中如何获取数据,无论是使用 fetch 还是连接数据库,这都将清除整个路由的缓存,并允许服务器组件获取新的数据。

app/actions.ts
'use server'
 
import { revalidatePath } from 'next/cache'
 
export async function createPost() {
  // 使缓存中的 /posts 路由失效
  revalidatePath('/posts')
}
app/actions.js
'use server'
 
import { revalidatePath } from 'next/cache'
 
export async function createPost() {
  // 使缓存中的 /posts 路由失效
  revalidatePath('/posts')
}

查看演示浏览源代码

使用 revalidateTag 进行按需重新验证

对于大多数用例,建议重新验证整个路径。如果你需要更细粒度的控制,可以使用 revalidateTag 函数。例如,你可以为单个 fetch 调用添加标签:

app/blog/page.tsx
TypeScript
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
TypeScript
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() {
  // 使缓存中所有标记为 'posts' 的数据失效
  revalidateTag('posts')
}
app/actions.js
'use server'
 
import { revalidateTag } from 'next/cache'
 
export async function createPost() {
  // 使缓存中所有标记为 'posts' 的数据失效
  revalidateTag('posts')
}

处理未捕获的异常

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

自定义缓存位置

缓存和重新验证页面(使用增量静态再生)使用相同的共享缓存。当部署到 Vercel 时,ISR 缓存会自动持久化到持久存储中。

在自托管时,ISR 缓存存储在 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

版本历史

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