Skip to main content

数据获取、缓存和重新验证

数据获取是任何应用程序的核心部分。

本页介绍了在React和Next.js中如何获取、缓存和重新验证数据

四种获取数据的方式

  1. 在服务器上使用fetch
  2. 在服务器上使用第三方库
  3. 通过路由处理程序在客户端上获取
  4. 在客户端上使用第三方库。

使用fetch在服务器上获取数据

Next.js扩展了原生的[fetch Web API允许您在服务器上为每个fetch请求配置 缓存和重新验证行为。 React通过在呈现React组件树时自动 记忆fetch请求来扩展fetch

您可以在Server Components、 Route HandlersServer Actions 中使用带有async/awaitfetch

app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/...')
// 返回值未序列化
// 可以返回Date、Map、Set等。

if (!res.ok) {
// 这将激活最接近的`error.js`错误边界
throw new Error('获取数据失败')
}

return res.json()
}

export default async function Page() {
const data = await getData()

return <main></main>
}
note
  1. Next.js提供了在Server Components中获取数据时可能需要的有用函数,如cookiesheaders。 由于它们依赖于请求时间信息,它们将导致路由动态渲染。
  2. 在Route Handlers中,fetch请求不会被记忆,因为Route Handlers不是React组件树的一部分。
  3. 要在带有TypeScript的Server Component中使用async/await, 您需要使用TypeScript 5.1.3或更高版本和@types/react 18.2.8或更高版本。

缓存数据

缓存会存储数据,使得不必在每个请求上都重新从数据源获取数据。

默认情况下,Next.js会自动将fetch返回的值缓存到服务器上的 Data Cache中。 这意味着数据可以在构建时间或请求时间获取,被缓存,并在每次数据请求时重复使用。

// 'force-cache'是默认值,可以省略
fetch('https://...', { cache: 'force-cache' })

使用POST方法的fetch请求也会自动缓存。 除非它在使用POST方法的 Route Handler 中,否则它将不会被缓存。

什么是Data Cache?

Data Cache是一个持久性 HTTP缓存。 根据您的平台,缓存可以自动扩展并在 多个地区共享。 了解有关Data Cache的更多信息。

重新验证数据

重新验证是清除Data Cache并重新获取最新数据的过程。 当数据发生变化并且您希望确保显示最新信息时,这很有用。

缓存的数据可以通过两种方式重新验证

  • 基于时间的重新验证:在经过一定时间后自动重新验证数据。这对于数据变化不频繁且新鲜度不是很关键的数据很有用。
  • 按需重新验证:根据事件(例如表单提交)手动重新验证数据。按需重新验证可以使用基于标签或基于路径的方法一次重新验证多个数据组。当您希望尽快显示最新数据时很有用(例如,当来自您的无头CMS的内容已更新时)。

基于时间的重新验证

要在定时间隔内重新验证数据,可以使用fetchnext.revalidate选项设置资源的缓存生存时间(以秒为单位)。

fetch('https://...', { next: { revalidate: 3600 } })

或者,要重新验证路由段中的所有fetch请求,可以使用 Segment Config Options

layout.js | page.js
export const revalidate = 3600 // 最多每小时重新验证一次

如果在静态渲染的路由中有多个fetch请求, 且每个请求具有不同的重新验证频率, 则将使用最低时间来重新验证所有请求。 对于动态渲染的路由,每个fetch请求将独立重新验证。

了解有关基于时间的重新验证的更多信息。

按需重新验证

可以通过路径(revalidatePath)或缓存标签(revalidateTag)在 Server ActionRoute Handler 中重新验证数据。

Next.js具有用于在跨路由无效化fetch请求的缓存标记系统。

  1. 在使用fetch时,您可以选择使用一个或多个标签标记缓存条目。
  2. 然后,您可以调用revalidateTag以重新验证与该标签关联的所有条目。

例如,以下fetch请求添加了缓存标签collection

app/page.tsx
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}

然后,您可以在Server Action中调用revalidateTag来重新验证带有collection标签的此fetch调用:

app/actions.ts
'use server'

import { revalidateTag } from 'next/cache'

export default async function action() {
revalidateTag('collection')
}

了解有关按需重新验证的更多信息。

错误处理和重新验证

如果在尝试重新验证数据时抛出错误,将继续从缓存中提供上次成功生成的数据。 在下一次请求时,Next.js将重试重新验证数据。

退出数据缓存

如果存在以下情况,则不会fetch请求进行缓存:

  1. cache: 'no-store'添加到fetch请求中。
  2. revalidate: 0选项添加到个别fetch请求。
  3. fetch请求在使用POST方法的Router Handler中。
  4. fetch请求在headerscookies的使用之后。
  5. 使用const dynamic = 'force-dynamic'路由段选项。
  6. 通过配置fetchCache路由段选项跳过缓存,默认情况下不会缓存。
  7. fetch请求使用AuthorizationCookie头,并且在组件树中有未缓存的请求。

个别fetch请求

要禁用个别fetch请求的缓存,可以在fetch的选项中

将cache选项设置为'no-store'。这将在每个请求上动态获取数据。

layout.js | page.js
fetch('https://...', { cache: 'no-store' })

fetch API参考 中查看所有可用的缓存选项。

多个fetch请求

如果在一个路由段(例如LayoutPage)中有多个fetch请求, 可以使用 Segment Config Options 配置段中所有数据请求的缓存行为。

但是,我们建议单独配置每个fetch请求的缓存行为。这样可以更精细地控制缓存行为。

在服务器上使用第三方库获取数据

在使用不支持或公开fetch的第三方库的情况下(例如数据库、CMS或ORM客户端), 可以使用Route Segment Config Option 和React的cache函数配置这些请求的缓存和重新验证行为。

数据是否被缓存将取决于路由段是 静态还是动态渲染。 如果段是静态的(默认值),则请求的输出将被缓存并作为路由段的一部分重新验证。 如果段是动态的,则请求的输出将不会被缓存,并且将在渲染段时的每个请求上重新获取。

还可以使用实验性的unstable_cache API

示例

在下面的示例中:

  • 使用React cache函数来记忆数据请求。
  • 在Layout和Page段中,将revalidate选项设置为3600,这意味着数据将每小时最多缓存和重新验证一次。
app/utils.ts
import { cache } from 'react'

export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})

尽管getItem函数调用了两次,但数据库只会进行一次查询。

app/item/[id]/layout.tsx
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // 每小时最多重新验证一次数据

export default async function Layout({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
app/item/[id]/page.tsx
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // 每小时最多重新验证一次数据

export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}

在客户端使用Route Handlers获取数据

如果需要在客户端组件中获取数据,可以从客户端调用 Route Handler。 Route Handlers在服务器上执行并将数据返回给客户端。当您不希望将敏感信息暴露给客户端时,例如API令牌时,这很有用。

请参阅Route Handler文档以获取示例。

Server Components和Route Handlers

由于Server Components在服务器上呈现,因此不需要从Server Component中调用Route Handler来获取数据。 相反,您可以直接在Server Component内部获取数据。

在客户端使用第三方库获取数据

您还可以使用第三方库(如SWRReact Query)在客户端获取数据。 这些库提供了它们自己的API,用于记忆请求、缓存、重新验证和变异数据。

未来的API:

use是一个接受并处理由函数返回的Promise的React函数。 当前不建议在Client Components中将fetch包装在use中,因为这可能触发多次重新渲染。 在React文档中了解有关use的更多信息。