Skip to main content

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

数据获取是任何应用的核心部分。本页介绍如何在 React 和 Next.js 中获取、缓存和重新验证数据。

¥Data fetching is a core part of any application. This page goes through how you can fetch, cache, and revalidate data in React and Next.js.

你可以通过四种方式获取数据:

¥There are four ways you can fetch data:

  1. 在服务器上,使用 fetch

    ¥On the server, with fetch

  2. 在服务器上,带有第三方库

    ¥On the server, with third-party libraries

  3. 在客户端上,通过路由处理程序

    ¥On the client, via a Route Handler

  4. 在客户端,使用第三方库

    ¥On the client, with third-party libraries.

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

¥Fetching Data on the Server with fetch

Next.js 扩展了原生 fetch 网络应用接口,允许你为服务器上的每个获取请求配置 cachingrevalidating 行为。React 扩展了 fetch,以便在渲染 React 组件树时自动获取 memoize 请求。

¥Next.js extends the native fetch Web API to allow you to configure the caching and revalidating behavior for each fetch request on the server. React extends fetch to automatically memoize fetch requests while rendering a React component tree.

你可以在服务器组件、路由处理程序服务器操作 中将 fetchasync/await 一起使用。

¥You can use fetch with async/await in Server Components, in Route Handlers, and in Server Actions.

例如:

¥For example:

async function getData() {
const res = await fetch('https://api.example.com/...')
// The return value is *not* serialized
// You can return Date, Map, Set, etc.

if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data')
}

return res.json()
}

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

return <main></main>
}
async function getData() {
const res = await fetch('https://api.example.com/...')
// The return value is *not* serialized
// You can return Date, Map, Set, etc.

if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data')
}

return res.json()
}

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

return <main></main>
}

很高兴知道:

¥Good to know:

  • Next.js 提供了在服务器组件(例如 cookiesheaders)中获取数据时可能需要的有用功能。这些将导致路由动态渲染,因为它们依赖于请求时间信息。

    ¥Next.js provides helpful functions you may need when fetching data in Server Components such as cookies and headers. These will cause the route to be dynamically rendered as they rely on request time information.

  • 在路由处理程序中,fetch 请求不会被记忆,因为路由处理程序不是 React 组件树的一部分。

    ¥In Route handlers, fetch requests are not memoized as Route Handlers are not part of the React component tree.

  • 服务器操作 中,fetch 请求不被缓存(默认 cache: no-store)。

    ¥In Server Actions, fetch requests are not cached (defaults cache: no-store).

  • 要在带有 TypeScript 的服务器组件中使用 async/await,你需要使用 TypeScript 5.1.3 或更高版本以及 @types/react 18.2.8 或更高版本。

    ¥To use async/await in a Server Component with TypeScript, you'll need to use TypeScript 5.1.3 or higher and @types/react 18.2.8 or higher.

缓存数据

¥Caching Data

缓存存储数据,因此不需要在每次请求时从数据源重新获取数据。

¥Caching stores data so it doesn't need to be re-fetched from your data source on every request.

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

¥By default, Next.js automatically caches the returned values of fetch in the Data Cache on the server. This means that the data can be fetched at build time or request time, cached, and reused on each data request.

// 'force-cache' is the default, and can be omitted
fetch('https://...', { cache: 'force-cache' })

不过,也有例外,在以下情况下,fetch 请求不会被缓存:

¥However, there are exceptions, fetch requests are not cached when:

什么是数据缓存?

¥What is the Data Cache?

数据缓存是持久的 HTTP 缓存。根据你的平台,缓存可以自动扩展并达到 跨多个区域共享

¥The Data Cache is a persistent HTTP cache. Depending on your platform, the cache can scale automatically and be shared across multiple regions.

了解有关 数据缓存 的更多信息。

¥Learn more about the Data Cache.

重新验证数据

¥Revalidating Data

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

¥Revalidation is the process of purging the Data Cache and re-fetching the latest data. This is useful when your data changes and you want to ensure you show the latest information.

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

¥Cached data can be revalidated in two ways:

  • 基于时间的重新验证:经过一定时间后自动重新验证数据。这对于不经常更改且新鲜度不那么重要的数据非常有用。

    ¥Time-based revalidation: Automatically revalidate data after a certain amount of time has passed. This is useful for data that changes infrequently and freshness is not as critical.

  • 按需重新验证:根据事件(例如表单提交)手动重新验证数据。按需重新验证可以使用基于标签或基于路径的方法立即重新验证数据组。当你想要确保尽快显示最新数据时(例如,当更新无头 CMS 的内容时),这非常有用。

    ¥On-demand revalidation: Manually revalidate data based on an event (e.g. form submission). On-demand revalidation can use a tag-based or path-based approach to revalidate groups of data at once. This is useful when you want to ensure the latest data is shown as soon as possible (e.g. when content from your headless CMS is updated).

基于时间的重新验证

¥Time-based Revalidation

要定期重新验证数据,你可以使用 fetchnext.revalidate 选项来设置资源的缓存生命周期(以秒为单位)。

¥To revalidate data at a timed interval, you can use the next.revalidate option of fetch to set the cache lifetime of a resource (in seconds).

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

或者,要重新验证路由段中的所有 fetch 请求,你可以使用 片段配置选项

¥Alternatively, to revalidate all fetch requests in a route segment, you can use the Segment Config Options.

export const revalidate = 3600 // revalidate at most every hour

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

¥If you have multiple fetch requests in a statically rendered route, and each has a different revalidation frequency. The lowest time will be used for all requests. For dynamically rendered routes, each fetch request will be revalidated independently.

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

¥Learn more about time-based revalidation.

按需重新验证

¥On-demand Revalidation

数据可以通过路径 (revalidatePath) 或通过 服务器动作路由处理程序 内的缓存标签 (revalidateTag) 按需重新验证。

¥Data can be revalidated on-demand by path (revalidatePath) or by cache tag (revalidateTag) inside a Server Action or Route Handler.

Next.js 有一个缓存标记系统,用于使跨路由的 fetch 请求无效。

¥Next.js has a cache tagging system for invalidating fetch requests across routes.

  1. 使用 fetch 时,你可以选择使用一个或多个标签来标记缓存条目。

    ¥When using fetch, you have the option to tag cache entries with one or more tags.

  2. 然后,你可以调用 revalidateTag 来重新验证与该标签关联的所有条目。

    ¥Then, you can call revalidateTag to revalidate all entries associated with that tag.

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

¥For example, the following fetch request adds the cache tag collection:

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

然后,你可以通过在服务器操作中调用 revalidateTag 来重新验证标记有 collectionfetch 调用:

¥You can then revalidate this fetch call tagged with collection by calling revalidateTag in a Server Action:

'use server'

import { revalidateTag } from 'next/cache'

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

import { revalidateTag } from 'next/cache'

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

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

¥Learn more about on-demand revalidation.

错误处理和重新验证

¥Error handling and revalidation

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

¥If an error is thrown while attempting to revalidate data, the last successfully generated data will continue to be served from the cache. On the next subsequent request, Next.js will retry revalidating the data.

选择退出数据缓存

¥Opting out of Data Caching

如果出现以下情况,则不会缓存 fetch 请求:

¥fetch requests are not cached if:

  • cache: 'no-store' 添加到 fetch 请求中。

    ¥The cache: 'no-store' is added to fetch requests.

  • revalidate: 0 选项添加到各个 fetch 请求中。

    ¥The revalidate: 0 option is added to individual fetch requests.

  • fetch 请求位于使用 POST 方法的路由处理程序内部。

    ¥The fetch request is inside a Router Handler that uses the POST method.

  • fetch 请求在使用 headerscookies 之后出现。

    ¥The fetch request comes after the usage of headers or cookies.

  • 使用 const dynamic = 'force-dynamic' 航路段选项。

    ¥The const dynamic = 'force-dynamic' route segment option is used.

  • fetchCache 路由段选项默认配置为跳过缓存。

    ¥The fetchCache route segment option is configured to skip cache by default.

  • fetch 请求使用 AuthorizationCookie 标头,并且组件树中其上方有一个未缓存的请求。

    ¥The fetch request uses Authorization or Cookie headers and there's an uncached request above it in the component tree.

个人 fetch 请求

¥Individual fetch Requests

要选择不缓存单个 fetch 请求,你可以将 fetch 中的 cache 选项设置为 'no-store'。这将根据每个请求动态获取数据。

¥To opt out of caching for individual fetch requests, you can set the cache option in fetch to 'no-store'. This will fetch data dynamically, on every request.

fetch('https://...', { cache: 'no-store' })

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

¥View all the available cache options in the fetch API reference.

多个 fetch 请求

¥Multiple fetch Requests

如果某个路由段(例如布局或页面)中有多个 fetch 请求,则可以使用 片段配置选项.config 配置该段中所有数据请求的缓存行为。

¥If you have multiple fetch requests in a route segment (e.g. a Layout or Page), you can configure the caching behavior of all data requests in the segment using the Segment Config Options.

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

¥However, we recommend configuring the caching behavior of each fetch request individually. This gives you more granular control over the caching behavior.

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

¥Fetching data on the Server with third-party libraries

如果你使用不支持或公开 fetch 的第三方库(例如数据库、CMS 或 ORM 客户端),你可以使用 路由段配置选项 和 React 的配置缓存和重新验证这些请求的行为。 cache 功能。

¥In cases where you're using a third-party library that doesn't support or expose fetch (for example, a database, CMS, or ORM client), you can configure the caching and revalidating behavior of those requests using the Route Segment Config Option and React's cache function.

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

¥Whether the data is cached or not will depend on whether the route segment is statically or dynamically rendered. If the segment is static (default), the output of the request will be cached and revalidated as part of the route segment. If the segment is dynamic, the output of the request will not be cached and will be re-fetched on every request when the segment is rendered.

你还可以使用实验性的 unstable_cache API

¥You can also use the experimental unstable_cache API.

示例

¥Example

在下面的例子中:

¥In the example below:

  • React cache 函数用于 memoize 数据请求。

    ¥The React cache function is used to memoize data requests.

  • 布局和页面段中的 revalidate 选项设置为 3600,这意味着数据将被缓存并最多每小时重新验证一次。

    ¥The revalidate option is set to 3600 in the Layout and Page segments, meaning the data will be cached and revalidated at most every hour.

import { cache } from 'react'

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

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

虽然 getItem 函数被调用了两次,但只会对数据库进行一次查询。

¥Although the getItem function is called twice, only one query will be made to the database.

import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // revalidate the data at most every hour

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

export const revalidate = 3600 // revalidate the data at most every hour

export default async function Layout({ params: { id } }) {
const item = await getItem(id)
// ...
}
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // revalidate the data at most every hour

export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // revalidate the data at most every hour

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

使用路由处理程序在客户端获取数据

¥Fetching Data on the Client with Route Handlers

如果需要在客户端组件中获取数据,可以从客户端调用 路由处理程序。路由处理程序在服务器上执行并将数据返回给客户端。当你不想向客户端公开敏感信息(例如 API 令牌)时,这非常有用。

¥If you need to fetch data in a client component, you can call a Route Handler from the client. Route Handlers execute on the server and return the data to the client. This is useful when you don't want to expose sensitive information to the client, such as API tokens.

有关示例,请参阅 路由处理程序 文档。

¥See the Route Handler documentation for examples.

服务器组件和路由处理程序

¥Server Components and Route Handlers

由于服务器组件在服务器上渲染,因此你不需要从服务器组件调用路由处理程序来获取数据。相反,你可以直接在服务器组件内获取数据。

¥Since Server Components render on the server, you don't need to call a Route Handler from a Server Component to fetch data. Instead, you can fetch the data directly inside the Server Component.

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

¥Fetching Data on the Client with third-party libraries

你还可以使用第三方库(例如 SWRTanStack 查询)在客户端获取数据。这些库提供了自己的 API,用于记忆请求、缓存、重新验证和更改数据。

¥You can also fetch data on the client using a third-party library such as SWR or TanStack Query. These libraries provide their own APIs for memoizing requests, caching, revalidating, and mutating data.

未来的 API:

¥Future APIs:

use 是一个 React 函数,它接受并处理函数返回的 Promise。目前不建议在客户端组件中将 fetch 封装在 use 中,并且可能会触发多次重新渲染。在 React 文档 中了解有关 use 的更多信息。

¥use is a React function that accepts and handles a promise returned by a function. Wrapping fetch in use is currently not recommended in Client Components and may trigger multiple re-renders. Learn more about use in the React docs.