Appearance
好的,根据您提供的两篇关于 Next.js 缓存机制的文档,我为您总结如下:
Next.js 拥有一套强大且复杂的缓存体系,旨在优化性能和降低成本。这套体系包含四种主要的缓存机制,它们在不同的层面、不同的位置工作,共同协作。
一、 四种缓存机制概览
| 机制 | 缓存内容 | 存储位置 | 主要目的 | 持续时间 |
|---|---|---|---|---|
| 1. 请求记忆 (Request Memoization) | 函数返回值 | 服务端内存 | 在单个请求的 React 组件树渲染过程中,避免对同一数据的重复请求 | 每个请求的生命周期 |
| 2. 数据缓存 (Data Cache) | 数据 (如 fetch 结果) | 服务端 | 跨用户请求和部署复用数据,减少对数据源的访问 | 持久,除非重新验证或退出 |
| 3. 完整路由缓存 (Full Route Cache) | HTML 和 RSC Payload | 服务端 | 复用整个路由的渲染结果,极大降低服务端渲染成本 | 持久,跨部署后清除 |
| 4. 路由缓存 (Router Cache) | RSC Payload | 客户端浏览器内存 | 减少导航时的服务端请求,实现快速、无刷新的页面切换 | 用户会话期间或基于时间(静态5分钟,动态30秒) |
二、 详细解析
1. 请求记忆 (Request Memoization)
- 本质:这是 React 的特性,而非 Next.js 特有。它通过“函数记忆”原理工作,对在同一个 React 组件树渲染过程中发起的、参数相同的
fetch请求进行去重。 - 作用域:仅在 React 组件树内有效(如
layout,page,generateMetadata等)。 - 持续时间:仅存在于一次服务器请求的整个渲染周期内。渲染结束后,缓存即被清除。
- 退出方式:不建议退出。若必须,可使用
AbortController创建不同的signal对象来“欺骗”缓存机制,使其认为是不同的请求。
2. 数据缓存 (Data Cache)
- 本质:这是 Next.js 提供的持久化缓存,可以跨多个用户请求和应用部署共享数据。
- 配置方式:
fetch(url, { cache: 'force-cache' }): 强制缓存(默认行为)。fetch(url, { cache: 'no-store' }): 强制不缓存,每次请求都从源获取。fetch(url, { next: { revalidate: 3600 } }): 基于时间的重新验证,例如每3600秒(1小时)后,有新请求到来时会触发后台更新。export const revalidate = 3600: 路由段配置,作用于该段内所有fetch请求。
- 按需重新验证:
- 基于标签 (
revalidateTag): 为fetch请求打上标签(next: { tags: ['news'] }),然后通过revalidateTag('news')来清除所有带此标签的缓存。 - 基于路径 (
revalidatePath): 通过revalidatePath('/dashboard')来清除特定路径相关的缓存。
- 基于标签 (
- 退出方式:使用
cache: 'no-store'或dynamic = 'force-dynamic'。
3. 完整路由缓存 (Full Route Cache)
- 内容:缓存的是路由在构建时或首次请求时生成的最终产物——HTML 和 RSC Payload。
- 适用范围:仅适用于静态渲染 (Static Rendering) 的路由。动态渲染的路由无法被完整路由缓存。
- 持续时间:持久化存储在服务端。
- 失效方式:
- 重新验证数据:当路由依赖的数据缓存被重新验证并更新时,完整路由缓存也会随之失效并重建。
- 重新部署:每次应用重新部署,完整路由缓存都会被清除。
- 退出方式:将路由转为动态渲染。方法包括:
- 使用
dynamic = 'force-dynamic'或revalidate = 0。 - 路由中包含
no-store的fetch请求。 - 使用
cookies或headers等动态函数。
- 使用
4. 路由缓存 (Router Cache)
- 本质:客户端的内存缓存,存储的是从服务端获取的 RSC Payload。
- 工作原理:当用户通过
<Link>导航时,Next.js 会预获取目标路由的 RSC Payload 并存入此缓存。再次访问时,直接从缓存中读取,无需网络请求,实现瞬时跳转。 - 持续时间:
- 静态路由:缓存5分钟。
- 动态路由:缓存30秒。
- 页面刷新后,缓存会被清除。
- 失效方式:
- 调用
router.refresh()。 - 在 Server Actions 中调用
revalidatePath或revalidateTag。 - 在 Server Actions 中修改
cookies。
- 调用
- 重要提示:无法完全退出。即使将
<Link prefetch={false}>,访问过的路由仍会临时缓存30秒以支持嵌套路由的即时导航。
三、 关键区别与实战要点
请求记忆 vs 数据缓存:
- 请求记忆是单次请求内的“去重”,是 React 的优化。
- 数据缓存是跨请求的“持久化”,是 Next.js 的核心功能。
- 两者通常同时存在。实战中,即使关闭了数据缓存,请求记忆仍可能导致多次调用返回相同数据。
完整路由缓存 vs 路由缓存:
- 完整路由缓存在服务端,针对静态路由,缓存的是HTML + RSC Payload。
- 路由缓存在客户端,针对所有路由(静/动),缓存的是RSC Payload。
开发痛点:路由缓存是“双刃剑”。它提供了丝滑的用户体验,但也常导致数据不更新(如导航后时间未变)。开发者必须理解其存在,并在需要实时数据时,通过
router.refresh()或修改cookies等方式主动使其失效。API 与缓存关系:文档末尾的表格是解决缓存问题的“万能钥匙”。当遇到数据未更新时,应先回顾使用了哪些 API,然后根据表格判断影响了哪种缓存,再选择对应的重新验证或退出策略。
总结:Next.js 的缓存机制设计精妙,自动化程度高。理解这四种缓存的层级、作用域和生命周期,是高效开发和调试 Next.js 应用的关键。开发者应善用缓存提升性能,同时也要掌握控制缓存的“开关”和“扳手”,以应对数据实时性等复杂场景。