文章探讨了Next.js的缓存机制,重点分析了HTTP缓存头(如Cache-Control、ETag)与SWR(Stale-While-Revalidate)策略的结合应用。通过对比强制缓存与协商缓存的差异,作者展示了如何利用SWR在后台异步更新数据以平衡性能与数据新鲜度。文章还解析了Next.js的unstable_cache服务端缓存配置,并提供了React Query和useSWR客户端库实现SWR的代码示例,帮助开发者优化应用性能。
最近在使用 nextjs 重构自己的博客,发现nextjs 有个强大的缓存系统,我们先从 SWR开始研究下。
首先查看下我博客的响应头输出,是不是感觉跟常见的响应头有点不一样
先聊聊常见的 http 缓存字段吧
下图查看交互时序
众所周知,强制缓存,如果设置不当就不会更新,如果过于保守则缓存不起作用,又增加了服务器压力,这其中蕴含着系统设计的哲学,那么我们能否既要又要呢?
因此,仅使用强制缓存header字段很难满足“ 既希望缓存生效,但又尽量提供最新数据”等需求。因此提出的就是 Stale-While-Revalidate (SwR) 这一
Cache-Control 的扩展。

简单来说,就是“ 先从缓存中显示内容,但会在后台异步更新缓存 ”的机制。
假设设置以下缓存控制头
Cache-Control: max-age=60, stale-while-revalidate=3600
这表示:
Stale-While-Revalidate (SWR) 其核心思想是,当资源过期后,缓存会立即返回(stale)的旧数据以保证快速响应,同时在后台(while-revalidate)异步请求新的数据以更新缓存,供后续请求使用。这种策略旨在平衡用户感知的性能和数据的最终一致性。SWR 的主要优势包括:
首先声明,nextjs 只是借助了 SWR 的概念,让 ISR 情况下去降低服务器的压力,
首先来详细梳理一下 Next.js 中 unstable_cache 的配置项与标准 HTTP 缓存头之间的微妙关系。
首先要明确最重要的一点:
虽然它们作用于不同层面,但 unstable_cache 的配置(特别是 revalidate)有可能会影响 Next.js 如何决定为包含该缓存数据的页面或路由设置哪些 HTTP 缓存头。
我们来看 unstable_cache 的主要配置项:
其中和 SWR 相关的就是revalidate配置项,它定义了服务器端数据缓存(s-maxage)的有效期(以秒为单位),在此期间,对该缓存函数的调用将直接返回缓存数据,也就是类似浏览器端http 的强缓存,只不过把这个作用在了服务端.
超过时间后,下一次调用会重新执行函数,并将新结果存入缓存(这个过程可以称为 Stale-While-Revalidate)。在重新验证完成前,旧的缓存数据可能仍会被短暂返回。
相对于 nextjs 可以使用 RSC 融合服务端响应头去使用 SWR 的特性,那单纯客户端请求的时候就很难利用浏览器的特性去控制这个缓存的颗粒度,所以基本上需要自己实现,其实也可以通过好用的第三方客户端请求库去更方便的模拟SWR,下面举两个常见的请求库
Vercel 的 swr 库本身就是以 Stale-While-Revalidate 命名的,所以它的默认行为就是 SWR。它没有一个直接叫做 staleTime 的选项来定义数据在多久内是 "fresh" 从而阻止自动 revalidation。相反,它通过 revalidateOnFocus, revalidateOnReconnect, refreshInterval 等选项来控制何时触发 "revalidate"。dedupingInterval 则用于防止在短时间内对同一 key 发起重复请求。