Next.js App Router 缓存机制完全指南

梳理 App Router 中四层缓存的边界与失效策略,解决「改了数据页面却不更新」这一高频困惑。

Next.js App Router 的缓存机制是新手最容易困惑的部分。很多「数据没更新」「页面是旧的」问题,根源都在于没分清四层缓存各自的职责。

四层缓存

App Router 在请求链路上有四个缓存层:

  1. 请求记忆(Request Memoization):单次请求内,相同 fetch 去重,避免重复请求;
  2. 数据缓存(Data Cache):跨请求缓存 fetch 结果,默认持久;
  3. 全路由缓存(Full Route Cache):服务端把整页 HTML/RSC 缓存起来;
  4. 路由缓存(Router Cache):客户端把已访问页面的 RSC 载荷缓存在内存里。

数据缓存:最常被踩的坑

默认情况下,fetch 结果会被持久缓存。这意味着即使后端数据变了,页面也可能显示旧数据。控制手段:

// 完全禁用缓存
fetch(url, { cache: 'no-store' });

// 按时间重新验证(ISR)
fetch(url, { next: { revalidate: 60 } });

对于数据库直连(无 fetch),则用 unstable_noStore() 或 export const revalidate = 60 控制。

按需失效:revalidatePath

如果你希望「数据变了主动让缓存失效」,用 revalidatePath:

async function updatePost(id) {
  await db.update(id);
  revalidatePath(`/blog/${id}`);
}

这比盲目地禁用缓存更优雅,兼顾了新鲜度与性能。

路由缓存与客户端跳转

客户端路由跳转时,浏览器会优先复用 Router Cache 中的旧页面。这也是「服务端已更新、客户端仍显示旧内容」的原因。刷新页面或等待失效窗口即可看到新内容。

决策清单

  • 数据实时性要求高 → no-store;
  • 数据可容忍短延迟 → revalidate;
  • 需要主动控制 → revalidatePath;
  • 完全静态 → 默认缓存 + 构建时生成。

结语

缓存是性能与新鲜度的永恒博弈。Next.js 给了你充分的控制权,前提是你分得清每一层缓存各自管什么。这张地图,值得打印出来贴在工位。