跳到内容
应用路由入门缓存和重新验证

缓存与重新验证

缓存是一种存储数据获取和其他计算结果的技术,以便将来对相同数据的请求能够更快地得到响应,而无需重复执行这些工作。而重新验证则允许您更新缓存条目,而无需重新构建整个应用程序。

Next.js 提供了几个 API 来处理缓存和重新验证。本指南将引导您了解何时以及如何使用它们。

fetch

默认情况下,fetch 请求不被缓存。您可以通过将 cache 选项设置为 'force-cache' 来缓存单个请求。

app/page.tsx
export default async function Page() {
  const data = await fetch('https://...', { cache: 'force-cache' })
}

须知:尽管 fetch 请求默认不被缓存,Next.js 会预渲染包含 fetch 请求的路由并缓存 HTML。如果您想确保某个路由是动态的,请使用 connection API

要重新验证 fetch 请求返回的数据,您可以使用 next.revalidate 选项。

app/page.tsx
export default async function Page() {
  const data = await fetch('https://...', { next: { revalidate: 3600 } })
}

这将在指定秒数后重新验证数据。

请参阅 fetch API 参考以了解更多信息。

unstable_cache

unstable_cache 允许您缓存数据库查询和其他异步函数的结果。要使用它,请将 unstable_cache 包装在函数周围。例如:

app/lib/data.ts
import { db } from '@/lib/db'
export async function getUserById(id: string) {
  return db
    .select()
    .from(users)
    .where(eq(users.id, id))
    .then((res) => res[0])
}
app/page.tsx
import { unstable_cache } from 'next/cache'
import { getUserById } from '@/app/lib/data'
 
export default async function Page({
  params,
}: {
  params: Promise<{ userId: string }>
}) {
  const { userId } = await params
 
  const getCachedUser = unstable_cache(
    async () => {
      return getUserById(userId)
    },
    [userId] // add the user ID to the cache key
  )
}

该函数接受一个可选的第三个对象来定义缓存应如何重新验证。它接受:

  • tags:Next.js 用于重新验证缓存的标签数组。
  • revalidate:缓存应重新验证的秒数。
app/page.tsx
const getCachedUser = unstable_cache(
  async () => {
    return getUserById(userId)
  },
  [userId],
  {
    tags: ['user'],
    revalidate: 3600,
  }
)

请参阅 unstable_cache API 参考以了解更多信息。

revalidateTag

revalidateTag 用于根据标签和事件重新验证缓存条目。该函数现在支持两种行为:

  • 使用 profile="max":采用陈旧时重新验证的语义,在后台获取新内容时提供陈旧内容。
  • 不带第二个参数:立即使缓存失效的旧行为(已弃用)。

要在 fetch 中使用它,请首先使用 next.tags 选项标记该函数。

app/lib/data.ts
export async function getUserById(id: string) {
  const data = await fetch(`https://...`, {
    next: {
      tags: ['user'],
    },
  })
}

或者,您可以使用 tags 选项标记 unstable_cache 函数。

app/lib/data.ts
export const getUserById = unstable_cache(
  async (id: string) => {
    return db.query.users.findFirst({ where: eq(users.id, id) })
  },
  ['user'], // Needed if variables are not passed as parameters
  {
    tags: ['user'],
  }
)

然后,在 路由处理程序 或 服务器动作中调用 revalidateTag

app/lib/actions.ts
import { revalidateTag } from 'next/cache'
 
export async function updateUser(id: string) {
  // Mutate data
  revalidateTag('user', 'max') // Recommended: Uses stale-while-revalidate
}

您可以在多个函数中重用相同的标签,以一次性重新验证所有这些函数。

请参阅 revalidateTag API 参考以了解更多信息。

revalidatePath

revalidatePath 用于在事件发生后重新验证路由。要使用它,请在 路由处理程序 或 服务器动作中调用它。

app/lib/actions.ts
import { revalidatePath } from 'next/cache'
 
export async function updateUser(id: string) {
  // Mutate data
  revalidatePath('/profile')

请参阅 revalidatePath API 参考以了解更多信息。

updateTag

updateTag 专门设计用于服务器动作,以便在“读写一致性”(read-your-own-writes)场景中立即使缓存数据失效。与 revalidateTag 不同,它只能在服务器动作中使用,并立即使缓存条目失效。

app/lib/actions.ts
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'
 
export async function createPost(formData: FormData) {
  // Create post in database
  const post = await db.post.create({
    data: {
      title: formData.get('title'),
      content: formData.get('content'),
    },
  })
 
  // Immediately expire cache so the new post is visible
  updateTag('posts')
  updateTag(`post-${post.id}`)
 
  redirect(`/posts/${post.id}`)
}

revalidateTagupdateTag 之间的主要区别:

  • updateTag:仅在服务器动作中使用,立即使缓存失效,适用于“读写一致性”场景。
  • revalidateTag:可在服务器动作和路由处理程序中使用,支持带有 profile="max" 的陈旧时重新验证。

请参阅 updateTag API 参考以了解更多信息。