04. 缓存机制深度解析 (Caching)
这是 Next.js App Router 中最复杂、最容易踩坑的部分。Next.js 为了极致的性能,默认会尽可能地缓存一切。
你需要了解 4 层缓存机制,才能随心所欲地控制数据的更新。
🏗️ 4 层缓存体系概览
| 机制 | 发生位置 | 持续时间 | 目标 | 怎么清除? |
|---|---|---|---|---|
| 1. Request Memoization | 服务器 | 单次请求生命周期 | 避免在一个页面渲染中重复调用同一个 fetch | 请求结束自动清除 |
| 2. Data Cache | 服务器 | 持久化 (跨请求) | 缓存 fetch 的结果 (类似 CDN) | revalidatePath / revalidateTag |
| 3. Full Route Cache | 服务器 | 持久化 (构建时) | 缓存渲染好的 HTML 和 RSC Payload | revalidatePath / 重新部署 |
| 4. Router Cache | 客户端(浏览器) | 会话期间 (Session) | 缓存访问过的页面,实现点击“秒开” | 刷新页面 / Mutation 后自动失效 |
1. Request Memoization (请求记忆)
作用:组件 A 和组件 B 都需要“用户信息”。你可以放心地在这两个组件里分别 fetch,Next.js 只会真的请求一次 API。
// 你的代码
async function getUser() {
const res = await fetch('https://api/user') // 第一次:真请求
return res.json()
}
// 组件 A
const user = await getUser()
// 组件 B
const user2 = await getUser() // 第二次:直接从内存读,不发网络请求注意:这只对 fetch 有效。如果你直接查数据库 (Prisma),需要用 React 的 cache 函数包裹。
2. Data Cache (数据缓存) 🔥 重点
作用:这是服务端的全局缓存。即使用户关掉浏览器再打开,只要没过期,数据还是旧的。
控制方式
通过 fetch 的 next 选项控制:
// A. 永久缓存 (默认, force-cache)
// 适用于:博客文章、产品详情
fetch('https://...', { cache: 'force-cache' })
// B. 永不缓存 (no-store)
// 适用于:实时股价、用户余额
fetch('https://...', { cache: 'no-store' })
// C. 定时缓存 (ISR)
// 适用于:新闻列表 (每 60 秒更新一次)
fetch('https://...', { next: { revalidate: 60 } })
// D. 按标签缓存 (On-Demand Revalidation) 🔥 最常用
// 适用于:CMS 内容更新
fetch('https://...', { next: { tags: ['posts'] } })如何手动清除?
当你在后台修改了文章,希望前台立刻更新,需要使用 Server Actions 或 Route Handler:
'use server'
import { revalidateTag } from 'next/cache'
async function updatePost() {
await db.post.update(...)
revalidateTag('posts') // ✨ 所有带 'posts' 标签的缓存瞬间失效
}3. Full Route Cache (全路由缓存)
作用:Next.js 在构建时 (Build Time) 就会把静态页面渲染成 HTML 存起来。
- 静态渲染 (Static): 如果你的页面没有用到 cookies, headers, searchParams,它就是静态的,会被永久缓存。
- 动态渲染 (Dynamic): 一旦你用了
cookies()或者fetch(..., { cache: 'no-store' }),页面就会变成动态的,每次请求都重新渲染,跳过此层缓存。
4. Router Cache (客户端路由缓存)
作用:你在浏览器点击
<Link>跳转时,Next.js 会把访问过的页面存存在浏览器内存里。当你点“后退”按钮时,页面是瞬间出现的,不需要去服务器取。
坑点:有时候你更新了数据跳转回列表页,发现列表没变?因为浏览器用了 Router Cache。
解决:使用 router.refresh() 或在 Server Action 中使用 revalidatePath (会自动清除客户端缓存)。
🎓 最佳实践总结
- 默认情况:Next.js 尽可能缓存。
- 实时数据:必须加
no-store。fetch(url, { cache: 'no-store' }) - CMS/后台内容:使用 Tags 并在修改后按需刷新。
// 获取时 fetch(url, { next: { tags: ['collection'] } }) // 修改后 (Server Action) revalidateTag('collection') - 调试技巧:开发环境 (Dev) 下缓存通常不生效,只有在
npm run build && npm run start后才能看到真实的缓存行为。