数据获取、缓存与重新验证
数据获取是任何应用的核心部分。本文将介绍如何在 React 和 Next.js 中获取、缓存及重新验证数据。
数据获取有以下四种方式:
在服务端使用 fetch
获取数据
Next.js 扩展了原生的 fetch
Web API,允许您为服务端的每个 fetch 请求配置缓存和重新验证行为。React 也扩展了 fetch
,在渲染 React 组件树时自动记忆化 fetch 请求。
您可以在服务端组件、路由处理器和服务端操作中使用 async
/await
配合 fetch
。
例如:
须知:
数据缓存
缓存存储数据,避免每次请求都从数据源重新获取。
默认情况下,Next.js 会自动在服务端的数据缓存中缓存 fetch
的返回值。这意味着数据可以在构建时或请求时获取、缓存,并在每次数据请求时重复使用。
但也有例外情况,fetch
请求不会被缓存:
什么是数据缓存?
数据缓存是一个持久的 HTTP 缓存。根据您的平台,缓存可以自动扩展并跨多个区域共享。
了解更多关于数据缓存的信息。
重新验证数据
重新验证是清除数据缓存并重新获取最新数据的过程。这在数据发生变化且您希望确保显示最新信息时非常有用。
缓存数据可以通过两种方式重新验证:
- 基于时间的重新验证:在一定时间间隔后自动重新验证数据。适用于变化不频繁且实时性要求不高的数据。
- 按需重新验证:根据事件(如表单提交)手动重新验证数据。按需重新验证可以使用基于标签或路径的方法一次性重新验证一组数据。适用于需要尽快显示最新数据的情况(例如无头 CMS 的内容更新时)。
基于时间的重新验证
要按时间间隔重新验证数据,可以使用 fetch
的 next.revalidate
选项设置资源的缓存生命周期(以秒为单位)。
或者,要重新验证路由段中的所有 fetch
请求,可以使用路由段配置选项。
如果在静态渲染的路由中有多个 fetch 请求,且每个请求有不同的重新验证频率,则所有请求将使用最短的时间。对于动态渲染的路由,每个 fetch
请求将独立重新验证。
了解更多关于基于时间的重新验证。
按需重新验证
可以在服务端操作或路由处理器中通过路径 (revalidatePath
) 或缓存标签 (revalidateTag
) 按需重新验证数据。
Next.js 有一个缓存标签系统,用于跨路由使 fetch
请求失效。
- 使用
fetch
时,可以选择用一个或多个标签标记缓存条目。 - 然后可以调用
revalidateTag
重新验证与该标签关联的所有条目。
例如,以下 fetch
请求添加了缓存标签 collection
:
然后可以在服务端操作中调用 revalidateTag
重新验证标记为 collection
的 fetch
调用:
了解更多关于按需重新验证。
错误处理与重新验证
如果在尝试重新验证数据时抛出错误,缓存将继续提供最后成功生成的数据。在后续请求中,Next.js 会重试重新验证数据。
退出数据缓存
fetch
请求在以下情况下不会被缓存:
fetch
请求中添加了cache: 'no-store'
。- 单个
fetch
请求中添加了revalidate: 0
选项。 fetch
请求位于使用POST
方法的路由处理器中。fetch
请求在使用了headers
或cookies
之后。- 使用了
const dynamic = 'force-dynamic'
路由段选项。 fetchCache
路由段选项配置为默认跳过缓存。fetch
请求使用了Authorization
或Cookie
标头,并且组件树中有未缓存的请求在其上方。
单个 fetch
请求
要为单个 fetch
请求退出缓存,可以将 fetch
的 cache
选项设置为 'no-store'
。这将在每次请求时动态获取数据。
查看所有可用的 cache
选项,请参阅 fetch
API 参考。
多个 fetch
请求
如果在路由段(如布局或页面)中有多个 fetch
请求,可以使用路由段配置选项配置该段中所有数据请求的缓存行为。
但我们建议单独配置每个 fetch
请求的缓存行为,这样可以更精细地控制缓存行为。
在服务端使用第三方库获取数据
如果您使用的第三方库不支持或不暴露 fetch
(例如数据库、CMS 或 ORM 客户端),可以使用路由段配置选项和 React 的 cache
函数配置这些请求的缓存和重新验证行为。
数据是否缓存取决于路由段是静态还是动态渲染。如果路由段是静态的(默认),请求的输出将作为路由段的一部分被缓存和重新验证。如果路由段是动态的,请求的输出将不会被缓存,并在每次渲染段时重新获取。
您还可以使用实验性的 unstable_cache
API。
示例
在以下示例中:
- React 的
cache
函数用于记忆化数据请求。 - 布局和页面段中的
revalidate
选项设置为3600
,意味着数据最多每小时缓存并重新验证一次。
尽管 getItem
函数被调用了两次,但只会向数据库发送一次查询。
在客户端通过路由处理器获取数据
如果需要在客户端组件中获取数据,可以从客户端调用路由处理器。路由处理器在服务端执行并将数据返回给客户端。这在您不想向客户端暴露敏感信息(如 API 令牌)时非常有用。
查看路由处理器文档以获取示例。
服务端组件与路由处理器
由于服务端组件在服务端渲染,您不需要从服务端组件调用路由处理器来获取数据。相反,可以直接在服务端组件中获取数据。
在客户端使用第三方库获取数据
您还可以使用第三方库(如 SWR 或 TanStack Query)在客户端获取数据。这些库提供了自己的 API 用于记忆化请求、缓存、重新验证和变更数据。
未来 API:
use
是一个 React 函数,接受并处理函数返回的 Promise。目前不建议在客户端组件中包装fetch
使用use
,可能会触发多次重新渲染。了解更多关于use
的信息,请参阅 React 文档。