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 PayloadrevalidatePath / 重新部署
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 (数据缓存) 🔥 重点

作用:这是服务端的全局缓存。即使用户关掉浏览器再打开,只要没过期,数据还是旧的。

控制方式

通过 fetchnext 选项控制:

// 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 (会自动清除客户端缓存)。


🎓 最佳实践总结

  1. 默认情况:Next.js 尽可能缓存。
  2. 实时数据:必须加 no-store
    fetch(url, { cache: 'no-store' })
  3. CMS/后台内容:使用 Tags 并在修改后按需刷新。
    // 获取时
    fetch(url, { next: { tags: ['collection'] } })
     
    // 修改后 (Server Action)
    revalidateTag('collection')
  4. 调试技巧:开发环境 (Dev) 下缓存通常不生效,只有在 npm run build && npm run start 后才能看到真实的缓存行为。