# next源码主线:缓存与revalidate机制
# 为什么这篇很关键
Next 项目线上最常见的问题不是“页面报错”,而是:
- 发布后数据没更新
- 某些节点更新、某些节点不更新
- 本地正常,线上缓存混乱
本质都指向一个问题:没有理解 Next 的多层缓存与失效链路。
# 先建立心智模型:Next 至少有 3 层缓存
# 1)Request Memoization(请求级去重)
同一次服务端渲染过程中,重复 fetch 相同请求可复用结果,避免 N+1。
特点:
- 生命周期短(一次请求内)
- 目标是“减少重复计算”,不是跨请求缓存
# 2)Data Cache(数据缓存)
fetch 配置 revalidate / tags 后,会形成跨请求可复用的数据缓存。
特点:
- 生命周期跨请求
- 由时间(TTL)或事件(tag/path)驱动失效
# 3)Route/Render Cache(路由渲染结果缓存)
页面或 segment 的渲染结果(含 RSC 产物)可能被缓存,减少重复 render 成本。
特点:
- 与路由层强相关
- 受动态 API、cookies/headers 使用影响
# 一句话
- Request Memoization:一次请求内去重
- Data Cache:跨请求的数据复用
- Route Cache:跨请求的渲染复用
只要混淆这三层,排障就一定混乱。
# revalidate 到底改变了什么
当你写:
await fetch(url, { next: { revalidate: 60 } });
1
你不是“每 60 秒强制请求一次”,而是:
- 60 秒窗口内优先返回缓存
- 窗口到期后触发再验证流程
再验证可能是“请求触发时重建”,不是固定定时任务。
# revalidateTag 与 revalidatePath 的差异
# revalidateTag
按“数据语义”失效,适合跨页面共享数据:
- 文章列表、作者信息、商品库存等
- 一次失效可影响多个页面
# revalidatePath
按“路由路径”失效,适合页面级精准刷新:
- 某个详情页路径
- 某个列表页路径
# 实战建议
- 业务数据驱动:优先 tag
- 页面单点刷新:再用 path
- 大型项目常组合使用:先 tag,再关键 path 兜底
# 失效触发链(源码理解版)
# 高层链路
- 写操作完成(DB/服务端成功)
- 调用
revalidateTag/revalidatePath - Next 标记对应缓存键失效
- 下次命中时走重算或重拉
- 新结果写回缓存
关键点:失效通常是“标记 + 下次重建”,不是立即全站推送刷新。
# 常见一致性问题(生产必踩)
# 问题 1:写成功但读旧数据
常见原因:
- 写操作后没触发 revalidate
- revalidate 调用失败但未告警
- 上游 CDN 仍缓存旧 HTML
# 问题 2:A 页面更新,B 页面没更新
常见原因:
- A/B 使用了不同 tag 命名
- 某个页面走了 path 缓存但未关联数据 tag
# 问题 3:多实例部署下表现不一致
常见原因:
- 失效事件未正确广播(或广播延迟)
- 各节点缓存层配置不一致
# 标签设计规范(非常重要)
推荐统一命名规则:
domain:resource:id[:scope]
1
示例:
cms:post:listcms:post:123shop:sku:9988:cn
不要用太泛的 tag(如 posts),否则失效范围不可控。
# 案例:内容平台发布链路(强一致要求)
需求:
- 作者发布文章后,列表和详情都要快速更新
- 不允许出现“详情新、列表旧”超过 10 秒
实现:
- 写库成功
- 触发:
revalidateTag("cms:post:list")revalidateTag("cms:post:{id}")revalidatePath("/posts")(兜底)
- 记录失效操作日志(含 requestId)
- 监控 10 秒内首个新请求是否返回新版本
核心是“失效行为可观测”,不是只靠肉眼刷新页面。
# 代码模板:发布后失效
import { revalidatePath, revalidateTag } from "next/cache";
export async function publishPost(postId: string) {
// 1) 持久化写入(略)
// 2) 失效数据缓存
revalidateTag("cms:post:list");
revalidateTag(`cms:post:${postId}`);
// 3) 可选:关键页面路径兜底
revalidatePath("/posts");
revalidatePath(`/posts/${postId}`);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 调试与观测清单
上线前至少验证:
- 冷缓存首访是否正确
- 热缓存命中是否正确
- 触发 revalidate 后首个请求是否拿到新数据
- 多实例下是否一致
建议埋点字段:
requestIdcacheLayer(memo/data/route/cdn)cacheHit(true/false)revalidateType(tag/path/time)tagOrPath
# 源码阅读路径建议(缓存主题)
建议按这条线追:
fetch包装层(如何注入 next cache 语义)- 缓存 key 构建逻辑(请求参数如何归一)
- revalidate API 到失效标记链路
- 下次请求如何检测失效并重建
- render 缓存与 data 缓存如何协同
读源码时不要追“所有文件”,只围绕这 5 个问题。
# 常见误区
- 误区 1:把
revalidate当强实时机制 - 误区 2:只配
revalidateTag不做监控 - 误区 3:把 CDN 缓存问题误判为 Next 缓存问题
- 误区 4:tag 命名无规范导致误失效或漏失效
# 小结
Next 缓存机制的难点不在 API,而在“多层缓存协同”。
你可以用一句话记住主线:
先分清缓存层,再设计失效语义,最后把失效变成可观测链路。