数据获取、缓存和重新验证
数据获取是任何应用的核心部分。本页介绍如何在 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:
使用 fetch
在服务器上获取数据
¥Fetching Data on the Server with fetch
Next.js 扩展了原生 fetch
网络应用接口,允许你为服务器上的每个获取请求配置 caching 和 revalidating 行为。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.
你可以在服务器组件、路由处理程序 和 服务器操作 中将 fetch
与 async
/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 提供了在服务器组件(例如
cookies
和headers
)中获取数据时可能需要的有用功能。这些将导致路由动态渲染,因为它们依赖于请求时间信息。¥Next.js provides helpful functions you may need when fetching data in Server Components such as
cookies
andheaders
. 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 (defaultscache: no-store
).要在带有 TypeScript 的服务器组件中使用
async
/await
,你需要使用 TypeScript5.1.3
或更高版本以及@types/react
18.2.8
或更高版本。¥To use
async
/await
in a Server Component with TypeScript, you'll need to use TypeScript5.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:
-
在 服务器动作 内部使用。
¥Used inside a Server Action.
-
在使用
POST
方法的 路由处理程序 内部使用。¥Used inside a Route Handler that uses the
POST
method.
什么是数据缓存?
¥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
要定期重新验证数据,你可以使用 fetch
的 next.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.
-
使用
fetch
时,你可以选择使用一个或多个标签来标记缓存条目。¥When using
fetch
, you have the option to tag cache entries with one or more tags. -
然后,你可以调用
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
来重新验证标记有 collection
的 fetch
调用:
¥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 tofetch
requests. -
revalidate: 0
选项添加到各个fetch
请求中。¥The
revalidate: 0
option is added to individualfetch
requests. -
fetch
请求位于使用POST
方法的路由处理程序内部。¥The
fetch
request is inside a Router Handler that uses thePOST
method. -
fetch
请求在使用headers
或cookies
之后出现。¥The
fetch
request comes after the usage ofheaders
orcookies
. -
使用
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
请求使用Authorization
或Cookie
标头,并且组件树中其上方有一个未缓存的请求。¥The
fetch
request usesAuthorization
orCookie
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 to3600
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
你还可以使用第三方库(例如 SWR 或 TanStack 查询)在客户端获取数据。这些库提供了自己的 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. Wrappingfetch
inuse
is currently not recommended in Client Components and may trigger multiple re-renders. Learn more aboutuse
in the React docs.