Next.js 中的缓存
Next.js 通过缓存渲染工作和数据请求来提高应用程序的性能并降低成本。此页面深入介绍了 Next.js 缓存机制、可用于配置这些机制的 API 以及它们如何相互作用。
须知:本页帮助您了解 Next.js 的底层工作原理,但不是高效使用 Next.js 的必要知识。Next.js 的大多数缓存启发式方法由您的 API 使用情况决定,并具有默认设置,可在零配置或最少配置的情况下实现最佳性能。如果您想直接跳转到示例,请从这里开始。
概述
以下是不同缓存机制及其用途的概要概述
机制 | 内容 | 位置 | 用途 | 持续时间 |
---|---|---|---|---|
请求记忆化 | 函数的返回值 | 服务器 | 在 React 组件树中重用数据 | 每次请求的生命周期 |
数据缓存 | 数据 | 服务器 | 跨用户请求和部署存储数据 | 持久性(可以重新验证) |
完整路由缓存 | HTML 和 RSC 有效负载 | 服务器 | 降低渲染成本并提高性能 | 持久性(可以重新验证) |
路由器缓存 | RSC 有效负载 | 客户端 | 减少导航时的服务器请求 | 用户会话或基于时间 |
默认情况下,Next.js 将尽可能多地进行缓存,以提高性能并降低成本。这意味着路由是静态渲染的,数据请求是缓存的,除非您选择退出。下图显示了默认缓存行为:在构建时静态渲染路由以及首次访问静态路由时。

缓存行为根据路由是静态渲染还是动态渲染、数据是缓存还是未缓存,以及请求是初始访问还是后续导航的一部分而变化。根据您的用例,您可以为单个路由和数据请求配置缓存行为。
请求记忆化
Next.js 扩展了 fetch
API 以自动记忆化具有相同 URL 和选项的请求。这意味着您可以在 React 组件树中的多个位置为相同的数据调用 fetch 函数,而只需执行一次。

例如,如果您需要在路由中使用相同的数据(例如,在布局、页面和多个组件中),则无需在树的顶部获取数据,并在组件之间转发 props。相反,您可以在需要数据的组件中获取数据,而无需担心为相同的数据跨网络发出多个请求的性能影响。
async function getItem() {
// The `fetch` function is automatically memoized and the result
// is cached
const res = await fetch('https://.../item/1')
return res.json()
}
// This function is called twice, but only executed the first time
const item = await getItem() // cache MISS
// The second call could be anywhere in your route
const item = await getItem() // cache HIT
请求记忆化如何工作

- 在渲染路由时,第一次调用特定请求时,其结果不会在内存中,这将是缓存
MISS
。 - 因此,将执行该函数,并将从外部源获取数据,并将结果存储在内存中。
- 在同一渲染过程中后续的请求函数调用将是缓存
HIT
,并且数据将从内存中返回,而无需执行该函数。 - 一旦路由被渲染并且渲染过程完成,内存将被“重置”,并且所有请求记忆化条目都将被清除。
须知:
- 请求记忆化是 React 的功能,而不是 Next.js 的功能。此处包含它是为了展示它如何与其他缓存机制交互。
- 记忆化仅适用于
fetch
请求中的GET
方法。- 记忆化仅适用于 React 组件树,这意味着
- 它适用于
generateMetadata
、generateStaticParams
、布局、页面和其他服务器组件中的fetch
请求。- 它不适用于路由处理程序中的
fetch
请求,因为它们不是 React 组件树的一部分。- 对于
fetch
不适用的情况(例如,某些数据库客户端、CMS 客户端或 GraphQL 客户端),您可以使用 Reactcache
函数来记忆化函数。
持续时间
缓存持续服务器请求的生命周期,直到 React 组件树完成渲染。
重新验证
由于记忆化不在服务器请求之间共享,并且仅在渲染期间应用,因此无需重新验证它。
选择退出
记忆化仅适用于 fetch
请求中的 GET
方法,其他方法(例如 POST
和 DELETE
)不进行记忆化。此默认行为是 React 优化,我们不建议选择退出。
要管理单个请求,您可以使用 signal
属性,来自 AbortController
。但是,这不会使请求退出记忆化,而是中止正在进行的请求。
const { signal } = new AbortController()
fetch(url, { signal })
数据缓存
Next.js 具有内置的数据缓存,可以持久化跨传入的服务器请求和部署的数据获取结果。之所以能够实现这一点,是因为 Next.js 扩展了原生 fetch
API,允许服务器上的每个请求设置其自身的持久性缓存语义。
须知:在浏览器中,
fetch
的cache
选项指示请求将如何与浏览器的 HTTP 缓存交互,在 Next.js 中,cache
选项指示服务器端请求将如何与服务器的数据缓存交互。
您可以使用 fetch
的 cache
和 next.revalidate
选项来配置缓存行为。
数据缓存如何工作

- 在渲染期间首次调用带有
'force-cache'
选项的fetch
请求时,Next.js 会检查数据缓存中是否有缓存的响应。 - 如果找到缓存的响应,则会立即返回并记忆化。
- 如果未找到缓存的响应,则会向数据源发出请求,结果将存储在数据缓存中并进行记忆化。
- 对于未缓存的数据(例如,未定义
cache
选项或使用{ cache: 'no-store' }
),结果始终从数据源获取并进行记忆化。 - 无论数据是否被缓存,请求始终会被记忆化,以避免在 React 渲染过程中为相同的数据发出重复请求。
数据缓存和请求记忆化之间的区别
虽然这两种缓存机制都有助于通过重用缓存数据来提高性能,但数据缓存在传入请求和部署之间是持久的,而记忆化仅持续请求的生命周期。
持续时间
数据缓存在传入请求和部署之间是持久的,除非您重新验证或选择退出。
重新验证
缓存的数据可以通过两种方式重新验证:
- 基于时间的重新验证:在经过一定时间并发出新请求后重新验证数据。这对于不经常更改且新鲜度不太重要的数据很有用。
- 按需重新验证:根据事件(例如,表单提交)重新验证数据。按需重新验证可以使用基于标签或基于路径的方法一次性重新验证数据组。当您想确保尽快显示最新数据时(例如,当您的无头 CMS 中的内容更新时),这非常有用。
基于时间的重新验证
要按时间间隔重新验证数据,您可以使用 fetch
的 next.revalidate
选项来设置资源的缓存生命周期(以秒为单位)。
// Revalidate at most every hour
fetch('https://...', { next: { revalidate: 3600 } })
或者,您可以使用路由段配置选项来配置段中的所有 fetch
请求,或者在您无法使用 fetch
的情况下进行配置。
基于时间的重新验证如何工作

- 首次调用带有
revalidate
的 fetch 请求时,数据将从外部数据源获取并存储在数据缓存中。 - 在指定时间范围内(例如 60 秒)调用的任何请求都将返回缓存的数据。
- 在时间范围之后,下一个请求仍将返回缓存的(现在已过时)数据。
- Next.js 将在后台触发数据重新验证。
- 成功获取数据后,Next.js 将使用新数据更新数据缓存。
- 如果后台重新验证失败,则先前的
This is similar to stale-while-revalidate behavior.
On-demand Revalidation
Data can be revalidated on-demand by path (revalidatePath
) or by cache tag (revalidateTag
).
How On-Demand Revalidation Works

- The first time a
fetch
request is called, the data will be fetched from the external data source and stored in the Data Cache. - When an on-demand revalidation is triggered, the appropriate cache entries will be purged from the cache.
- This is different from time-based revalidation, which keeps the stale data in the cache until the fresh data is fetched.
- The next time a request is made, it will be a cache
MISS
again, and the data will be fetched from the external data source and stored in the Data Cache.
选择退出
If you do not want to cache the response from fetch
, you can do the following
let data = await fetch('https://api.vercel.app/blog', { cache: 'no-store' })
Full Route Cache
Related terms:
You may see the terms Automatic Static Optimization, Static Site Generation, or Static Rendering being used interchangeably to refer to the process of rendering and caching routes of your application at build time.
Next.js automatically renders and caches routes at build time. This is an optimization that allows you to serve the cached route instead of rendering on the server for every request, resulting in faster page loads.
To understand how the Full Route Cache works, it's helpful to look at how React handles rendering, and how Next.js caches the result
1. React Rendering on the Server
On the server, Next.js uses React's APIs to orchestrate rendering. The rendering work is split into chunks: by individual routes segments and Suspense boundaries.
Each chunk is rendered in two steps
- React renders Server Components into a special data format, optimized for streaming, called the React Server Component Payload.
- Next.js uses the React Server Component Payload and Client Component JavaScript instructions to render HTML on the server.
This means we don't have to wait for everything to render before caching the work or sending a response. Instead, we can stream a response as work is completed.
What is the React Server Component Payload?
The React Server Component Payload is a compact binary representation of the rendered React Server Components tree. It's used by React on the client to update the browser's DOM. The React Server Component Payload contains
- The rendered result of Server Components
- Placeholders for where Client Components should be rendered and references to their JavaScript files
- Any props passed from a Server Component to a Client Component
To learn more, see the Server Components documentation.
2. Next.js Caching on the Server (Full Route Cache)

The default behavior of Next.js is to cache the rendered result (React Server Component Payload and HTML) of a route on the server. This applies to statically rendered routes at build time, or during revalidation.
3. React Hydration and Reconciliation on the Client
At request time, on the client
- The HTML is used to immediately show a fast non-interactive initial preview of the Client and Server Components.
- The React Server Components Payload is used to reconcile the Client and rendered Server Component trees, and update the DOM.
- The JavaScript instructions are used to hydrate Client Components and make the application interactive.
4. Next.js Caching on the Client (Router Cache)
The React Server Component Payload is stored in the client-side Router Cache - a separate in-memory cache, split by individual route segment. This Router Cache is used to improve the navigation experience by storing previously visited routes and prefetching future routes.
5. Subsequent Navigations
On subsequent navigations or during prefetching, Next.js will check if the React Server Components Payload is stored in the Router Cache. If so, it will skip sending a new request to the server.
If the route segments are not in the cache, Next.js will fetch the React Server Components Payload from the server, and populate the Router Cache on the client.
Static and Dynamic Rendering
Whether a route is cached or not at build time depends on whether it's statically or dynamically rendered. Static routes are cached by default, whereas dynamic routes are rendered at request time, and not cached.
This diagram shows the difference between statically and dynamically rendered routes, with cached and uncached data

Learn more about static and dynamic rendering.
持续时间
By default, the Full Route Cache is persistent. This means that the render output is cached across user requests.
Invalidation
There are two ways you can invalidate the Full Route Cache
- Revalidating Data: Revalidating the Data Cache, will in turn invalidate the Router Cache by re-rendering components on the server and caching the new render output.
- Redeploying: Unlike the Data Cache, which persists across deployments, the Full Route Cache is cleared on new deployments.
选择退出
You can opt out of the Full Route Cache, or in other words, dynamically render components for every incoming request, by
- Using a Dynamic API: This will opt the route out from the Full Route Cache and dynamically render it at request time. The Data Cache can still be used.
- Using the
dynamic = 'force-dynamic'
orrevalidate = 0
route segment config options: This will skip the Full Route Cache and the Data Cache. Meaning components will be rendered and data fetched on every incoming request to the server. The Router Cache will still apply as it's a client-side cache. - 选择退出 数据缓存:如果路由的
fetch
请求未被缓存,则该路由将选择退出完整路由缓存。对于每个传入的请求,都会重新获取特定fetch
请求的数据。其他未选择退出缓存的fetch
请求仍将缓存在数据缓存中。这允许缓存和非缓存数据的混合使用。
客户端 Router 缓存
Next.js 具有一个内存中的客户端 Router 缓存,用于存储路由段的 RSC payload,并按布局、加载状态和页面进行拆分。
当用户在路由之间导航时,Next.js 会缓存已访问的路由段,并预取用户可能导航到的路由。这实现了即时的前进/后退导航,导航之间没有完整的页面重新加载,并保留了 React 状态和浏览器状态。
使用 Router 缓存
- 布局(Layouts)在导航时会被缓存和重用(部分渲染)。
- 加载状态(Loading states)在导航时会被缓存和重用,以实现即时导航。
- 页面(Pages)默认情况下不缓存,但在浏览器后退和前进导航期间会重用。您可以使用实验性的
staleTimes
配置选项为页面段启用缓存。
须知: 此缓存专门应用于 Next.js 和 Server Components,并且与浏览器的 bfcache 不同,尽管它们具有类似的结果。
持续时间
缓存存储在浏览器的临时内存中。以下两个因素决定了 Router 缓存的持续时间:
- 会话(Session):缓存会在导航期间持久存在。但是,在页面刷新时会被清除。
- 自动失效期(Automatic Invalidation Period):布局和加载状态的缓存会在特定时间后自动失效。持续时间取决于资源的预取方式,以及资源是否是静态生成的。
- 默认预取(Default Prefetching) (
prefetch={null}
或未指定):动态页面不缓存,静态页面缓存 5 分钟。 - 完全预取(Full Prefetching) (
prefetch={true}
或router.prefetch
):静态和动态页面均缓存 5 分钟。
- 默认预取(Default Prefetching) (
虽然页面刷新会清除所有缓存的段,但自动失效期仅影响从预取时开始的单个段。
须知:实验性的
staleTimes
配置选项可用于调整上述自动失效时间。
Invalidation
您可以通过两种方式使 Router 缓存失效:
- 在 Server Action 中
- 通过路径使用 (
revalidatePath
) 或通过缓存标签使用 (revalidateTag
) 按需重新验证数据 - 使用
cookies.set
或cookies.delete
会使 Router 缓存失效,以防止使用 cookie 的路由变得陈旧(例如,身份验证)。
- 通过路径使用 (
- 调用
router.refresh
将使 Router 缓存失效,并为当前路由向服务器发出新的请求。
选择退出
从 Next.js 15 开始,默认情况下页面段选择退出缓存。
须知: 您还可以通过将
<Link>
组件的prefetch
属性设置为false
来选择退出预取。
缓存交互
在配置不同的缓存机制时,了解它们如何相互交互非常重要。
数据缓存和完整路由缓存
- 重新验证或选择退出数据缓存将使完整路由缓存失效,因为渲染输出取决于数据。
- 使完整路由缓存失效或选择退出不会影响数据缓存。您可以动态渲染同时具有缓存和非缓存数据的路由。当您的大部分页面使用缓存数据,但您有一些组件依赖于需要在请求时获取的数据时,这非常有用。您可以动态渲染,而无需担心重新获取所有数据对性能的影响。
数据缓存和客户端 Router 缓存
- 要立即使数据缓存和 Router 缓存失效,您可以在 Server Action 中使用
revalidatePath
或revalidateTag
。 - 在 Route Handler 中重新验证数据不会立即使 Router 缓存失效,因为 Route Handler 未绑定到特定路由。这意味着 Router 缓存将继续提供之前的 payload,直到硬刷新或自动失效期届满。
API
下表概述了不同的 Next.js API 如何影响缓存:
API | 路由器缓存 | 完整路由缓存 | 数据缓存 | React 缓存 |
---|---|---|---|---|
<Link prefetch> | 缓存 | |||
router.prefetch | 缓存 | |||
router.refresh | 重新验证 | |||
fetch | 缓存 | 缓存 | ||
fetch options.cache | 缓存或选择退出 | |||
fetch options.next.revalidate | 重新验证 | 重新验证 | ||
fetch options.next.tags | 缓存 | 缓存 | ||
revalidateTag | 重新验证 (Server Action) | 重新验证 | 重新验证 | |
revalidatePath | 重新验证 (Server Action) | 重新验证 | 重新验证 | |
const revalidate | 重新验证或选择退出 | 重新验证或选择退出 | ||
const dynamic | 缓存或选择退出 | 缓存或选择退出 | ||
cookies | 重新验证 (Server Action) | 选择退出 | ||
headers , searchParams | 选择退出 | |||
generateStaticParams | 缓存 | |||
React.cache | 缓存 | |||
unstable_cache | 缓存 |
<Link>
默认情况下,<Link>
组件会自动从完整路由缓存中预取路由,并将 React Server Component Payload 添加到 Router 缓存中。
要禁用预取,您可以将 prefetch
属性设置为 false
。但这不会永久跳过缓存,当用户访问该路由时,路由段仍将在客户端缓存。
了解更多关于 <Link>
组件 的信息。
router.prefetch
useRouter
hook 的 prefetch
选项可用于手动预取路由。这会将 React Server Component Payload 添加到 Router 缓存中。
请参阅 useRouter
hook API 参考。
router.refresh
useRouter
hook 的 refresh
选项可用于手动刷新路由。这将完全清除 Router 缓存,并为当前路由向服务器发出新的请求。refresh
不会影响数据缓存或完整路由缓存。
渲染结果将在客户端进行协调,同时保留 React 状态和浏览器状态。
请参阅 useRouter
hook API 参考。
fetch
从 fetch
返回的数据不会自动缓存在数据缓存中。
fetch
的默认缓存行为(例如,当未指定 cache
选项时)等同于将 cache
选项设置为 no-store
。
let data = await fetch('https://api.vercel.app/blog', { cache: 'no-store' })
有关更多选项,请参阅 fetch
API 参考。
fetch options.cache
您可以通过将 cache
选项设置为 force-cache
来选择将单个 fetch
纳入缓存。
// Opt into caching
fetch(`https://...`, { cache: 'force-cache' })
有关更多选项,请参阅 fetch
API 参考。
fetch options.next.revalidate
您可以使用 fetch
的 next.revalidate
选项来设置单个 fetch
请求的重新验证周期(以秒为单位)。这将重新验证数据缓存,进而重新验证完整路由缓存。将获取新数据,并在服务器上重新渲染组件。
// Revalidate at most after 1 hour
fetch(`https://...`, { next: { revalidate: 3600 } })
有关更多选项,请参阅 fetch
API 参考。
fetch options.next.tags
和 revalidateTag
Next.js 具有缓存标签系统,用于细粒度的数据缓存和重新验证。
- 当使用
fetch
或unstable_cache
时,您可以选择使用一个或多个标签标记缓存条目。 - 然后,您可以调用
revalidateTag
来清除与该标签关联的缓存条目。
例如,您可以在获取数据时设置标签:
// Cache data with a tag
fetch(`https://...`, { next: { tags: ['a', 'b', 'c'] } })
然后,使用标签调用 revalidateTag
以清除缓存条目:
// Revalidate entries with a specific tag
revalidateTag('a')
根据您尝试实现的目标,您可以在两个地方使用 revalidateTag
:
- Route Handlers - 用于响应第三方事件(例如,webhook)重新验证数据。这不会立即使 Router 缓存失效,因为 Router Handler 未绑定到特定路由。
- Server Actions - 用于在用户操作(例如,表单提交)后重新验证数据。这将使关联路由的 Router 缓存失效。
revalidatePath
revalidatePath
允许您在单个操作中手动重新验证数据并重新渲染特定路径下的路由段。调用 revalidatePath
方法会重新验证数据缓存,进而使完整路由缓存失效。
revalidatePath('/')
根据您尝试实现的目标,您可以在两个地方使用 revalidatePath
:
- Route Handlers - 用于响应第三方事件(例如,webhook)重新验证数据。
- Server Actions - 用于在用户交互(例如,表单提交、单击按钮)后重新验证数据。
有关更多信息,请参阅 revalidatePath
API 参考。
revalidatePath
vs.router.refresh
调用
router.refresh
将清除 Router 缓存,并在服务器上重新渲染路由段,而不会使数据缓存或完整路由缓存失效。区别在于
revalidatePath
会清除数据缓存和完整路由缓存,而router.refresh()
不会更改数据缓存和完整路由缓存,因为它是一个客户端 API。
动态 API
动态 API(如 cookies
和 headers
)以及 Pages 中的 searchParams
prop 依赖于运行时传入的请求信息。使用它们将使路由选择退出完整路由缓存,换句话说,路由将被动态渲染。
cookies
在 Server Action 中使用 cookies.set
或 cookies.delete
会使 Router 缓存失效,以防止使用 cookie 的路由变得陈旧(例如,反映身份验证更改)。
请参阅 cookies
API 参考。
段配置选项
路由段配置选项可用于覆盖路由段默认值,或者当您无法使用 fetch
API 时(例如,数据库客户端或第三方库)。
以下路由段配置选项将选择退出完整路由缓存:
const dynamic = 'force-dynamic'
此配置选项将使所有 fetch 都选择退出数据缓存(即 no-store
):
const fetchCache = 'default-no-store'
请参阅 fetchCache
以查看更多高级选项。
有关更多选项,请参阅 路由段配置 文档。
generateStaticParams
对于动态段(例如,app/blog/[slug]/page.js
),由 generateStaticParams
提供的路径在构建时缓存在完整路由缓存中。在请求时,Next.js 也会在首次访问时缓存构建时未知的路径。
要在构建时静态渲染所有路径,请将完整路径列表提供给 generateStaticParams
:
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
要在构建时静态渲染路径的子集,并在运行时首次访问时渲染其余路径,请返回路径的部分列表:
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
// Render the first 10 posts at build time
return posts.slice(0, 10).map((post) => ({
slug: post.slug,
}))
}
要静态渲染首次访问时的所有路径,请返回一个空数组(构建时不会渲染任何路径)或使用 export const dynamic = 'force-static'
。
export async function generateStaticParams() {
return []
}
须知: 您必须从
generateStaticParams
返回一个数组,即使它是空的。否则,路由将被动态渲染。
export const dynamic = 'force-static'
要禁用请求时的缓存,请在路由段中添加 export const dynamicParams = false
选项。当使用此配置选项时,将仅提供由 generateStaticParams
提供的路径,其他路由将返回 404 或匹配(对于catch-all 路由的情况)。
React cache
函数
React cache
函数允许您记忆化函数的返回值,从而允许您多次调用同一函数,而仅执行一次。
由于 fetch
请求会自动记忆化,因此您无需将其包装在 React cache
中。但是,当 fetch
API 不适用时,您可以使用 cache
手动记忆化数据请求以用于用例。例如,某些数据库客户端、CMS 客户端或 GraphQL 客户端。
import { cache } from 'react'
import db from '@/lib/db'
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
这篇文章对您有帮助吗?