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


缓存行为会根据路由是静态还是动态渲染、数据是否被缓存、以及请求是初始访问还是后续导航而变化。根据你的使用场景,你可以为单个路由和数据请求配置缓存行为。
在 middleware
中不支持 fetch 缓存。在 middleware
内部进行的任何 fetch 请求都不会被缓存。
请求记忆化
Next.js 扩展了 fetch
API,自动记忆化具有相同 URL 和选项的请求。这意味着你可以在 React 组件树的多个位置调用同一个 fetch 函数来获取相同的数据,而只会执行一次。


例如,如果你需要在路由中使用相同的数据(例如在 Layout、Page 和多个组件中),你不必在树的顶部获取数据,并在组件之间传递 props。相反,你可以在需要数据的组件中获取数据,而不必担心为相同数据在网络中发出多个请求的性能影响。
- TypeScript
- JavaScript
async function getItem() {
// `fetch` 函数会自动记忆化,结果会被缓存
const res = await fetch('https://.../item/1')
return res.json()
}
// 这个函数被调用了两次,但只在第一次执行
const item = await getItem() // 缓存 MISS
// 第二次调用可能在你路由的任何地方
const item = await getItem() // 缓存 HIT
async function getItem() {
// `fetch` 函数会自动记忆化,结果会被缓存
const res = await fetch('https://.../item/1')
return res.json()
}
// 这个函数被调用了两次,但只在第一次执行
const item = await getItem() // 缓存 MISS
// 第二次调用可能在你路由的任何地方
const item = await getItem() // 缓存 HIT
请求记忆化的工作原理


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


- 在渲染期间第一次调用带有
'force-cache'
选项的fetch
请求时,Next.js 会检查数据缓存中是否有缓存的响应。 - 如果找到缓存的响应,它会立即返回并被记忆化。
- 如果没有找 到缓存的响应,请求会发送到数据源,结果会存储在数据缓存中并被记忆化。
- 对于未缓存的数据(例如未定义
cache
选项或使用{ cache: 'no-store' }
),结果总是从数据源获取并被记忆化。 - 无论数据是否被缓存,请求总是被记忆化以避免在 React 渲染过程中为相同数据发出重复请求。
数据缓存和请求记忆化之间的区别
虽然两种缓存机制都通过重用缓存数据来帮助提升性能,但数据缓存在传入请求和部署之间是持久化的,而记忆化仅在请求的生命周期内持续。
持续时间
数据缓存在传入请求和部署之间是持久化的,除非你重新验证或选择退出。
重新验证
缓存数据可以通过两种方式重新验证:
- 基于时间的重新验证:在特定时间过去后重新验证数据,并发出新请求。这对于不经常变化且新鲜度不是那么关键的数据很有用。
- 按需重新验证:基于事件重新验证数据(例如表单提交)。按需重新验证可以使用基于标签或基于路径的方法来一次性重新验证数据组。当你希望确保尽快显示最新数据时(例如当你的无头 CMS 的内容更新时),这很有用。