先记住这个答案
在 Next.js 中,先为数据请求标注标签,如 fetch(url, { next: { tags: ['posts'] } }),或使用 cacheTag。之后在 Server Action 或 Route Handler 中调用 revalidateTag('posts', 'max'),系统会将该标签下的所有缓存标记为过期。下次请求时,相关数据以 stale-while-revalidate 方式返回旧内容并在后台更新。此操作只影响标有该标签的数据,不影响页面上其他未标注标签的缓存,从而实现局部失效。
- 用标签分组缓存,再精准失效。
- 失效按请求触发,非调用时立即更新。
revalidateTag不适用于客户端组件。- 推荐使用双参数形式,避免弃用签名。
失效的触发与数据流
在 Next.js 中,缓存条目可以附带多个标签。revalidateTag 传入一个字符串标签,系统会遍历数据缓存,将匹配该标签的条目标记为过期。注意,过期并不是立即从缓存删除,而是采用 stale-while-revalidate 策略:下一次请求会先返回旧值,同时启动后台重新验证,更新缓存。前提是你传入 profile="max" 或类似的配置。
这个设计使得失效的代价与请求频率解耦。调用 revalidateTag 本身不触发任何网络或渲染,只是修改了缓存状态。真正的数据获取发生在后续请求到来时。因此,如果你调用后没有新请求,旧数据会一直保留在缓存中,直到被强制覆盖或过期策略到期。多个页面共用同一标签时,所有使用了该标签数据的请求都会被触发更新,但只在各自被访问时发生,不是立即并发刷新。
博客系统:只更新评论而不重写全文
假设一个博客文章页 /posts/[slug] 展示文章正文和评论区。文章正文不常变,评论却时时新增。如果不加区分,更新评论会迫使整页重新验证,拉低性能。这里可以给文章正文请求打上 post-content 标签,给评论请求打上 post-comments 标签,两个请求都位于同一个页面组件中。新增评论后,在 Server Action 中只调用 revalidateTag('post-comments', 'max')。
这个操作不会让全文缓存失效,因此已经访问过该页的用户下一次请求时,页面仍从缓存读取正文,而评论则先显示旧值,然后在后台更新为最新,用户体验几乎无感。文章内容发布更新时,才去 revalidateTag('post-content', 'max') 或重新验证路径。这样不同数据块的更新频率可以独立控制,避免不必要的全页重算,也降低了数据库负载。
失效失效:标签覆盖、并发与外部触发
最直接的边界是标签没有正确分配。如果某个数据请求没有打标签,或者打错了字符串(区分大小写且不能超过 256 字符),revalidateTag 不会产生任何效果,而且不会报错。另外,如果你将一个未使用的标签传入函数,调用会静默成功,但什么也不会发生。调试时可以检查是否所有需要失效的请求都通过 next.tags 或 cacheTag 明确声明了目标标签。
另一个限制是执行环境。revalidateTag 只能在 Server Action 或 Route Handler 中使用,不能在客户端组件或代理中调用。若由外部 webhook 触发,只能走 Route Handler。此时若需要立即过期而不过渡服务旧值,应传入 { expire: 0 },否则使用推荐的 profile="max" 可以让用户继续看到旧内容直到后台刷新完成,代价是极端情况下旧数据可能展示很久。对于写多读少的场景,必须权衡陈旧窗口与响应延迟。
容易答错的地方
- 混淆 `revalidatePath` 与 `revalidateTag`
- 有人认为
revalidateTag是revalidatePath的简写或别名,这是错误的。revalidatePath针对特定路由的整个缓存条目,不关心数据来源;而revalidateTag针对带有特定标签的数据,可跨多个路径。如果页面包含多个独立数据块,revalidateTag能实现更细粒度控制。 - 误以为调用 `revalidateTag` 会立即刷新页面
revalidateTag只将标记数据置为失效,并不会主动重新生成缓存。下一次对该数据的请求才会触发重新验证,并在验证完成前返回旧数据。这符合stale-while-revalidate语义,如果需要立即返回最新数据,应使用{ expire: 0 }或配合其他更新策略。
面试官还会怎么问?
`revalidateTag` 的旧版单参数写法还能用吗?
官方文档标注为 deprecated,行为等同于 { expire: 0 },但在未来可能移除。建议使用双参数形式 revalidateTag(tag, profile),其中 profile 可以是 "max"、自定义 cacheLife profile 或 { expire }。
如果一个页面同时使用多个标签,失效其中一个会怎样?
只失效匹配该标签的缓存条目,其他标签对应的数据保持有效。页面的渲染仍可能依赖这些有效数据,且整体页面缓存不会受影响。如果所有数据都失效,页面才会在下次请求时重新执行数据获取。
如何验证 `revalidateTag` 确实只影响了部分数据?
可以在页面中分别给两块数据打不同标签,然后调用失效,观察响应头或记录日志。通过 next cache 相关工具或打点,比较失效前后请求状态码与耗时,确认仅目标标签的请求发生重新验证,其余仍命中缓存。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。