Eavan's Blog LogoEavan's Blog
TAGSFRIENDSBOOKSSEARCH
TAGS
FRIENDS
BOOKS
SEARCH
深色
Eavan's Blog LogoEAVAN
·

心如止水,字如清风

流水不争先 ·

聊聊http缓存中的Stale-While-Revalidate

03/24/2025
5 分钟
目录

标签

相关

参考

精选

DeepSeek

AI 摘要 (由 deepseek/deepseek-chat-v3-0324 生成)

文章探讨了Next.js的缓存机制,重点分析了HTTP缓存头(如Cache-Control、ETag)与SWR(Stale-While-Revalidate)策略的结合应用。通过对比强制缓存与协商缓存的差异,作者展示了如何利用SWR在后台异步更新数据以平衡性能与数据新鲜度。文章还解析了Next.js的unstable_cache服务端缓存配置,并提供了React Query和useSWR客户端库实现SWR的代码示例,帮助开发者优化应用性能。

10.34s
~7261 tokens

起因

最近在使用 nextjs 重构自己的博客,发现nextjs 有个强大的缓存系统,我们先从 网站图标SWR开始研究下。

nextjs.org
Learn how to fetch data and stream content that depends on data.
网站图标nextjs.org

首先查看下我博客的响应头输出,是不是感觉跟常见的响应头有点不一样

常见的 http 缓存

先聊聊常见的 http 缓存字段吧

强制缓存(Force Cache):

    • Expires:HTTP/1.0的缓存控制字段,设置资源的过期时间点
    • Cache-Control:HTTP/1.1的缓存控制字段,比Expires优先级更高
      • max-age:资源最大有效时间(秒)
      • s-maxage:代理服务器缓存的最大有效时间
      • no-cache:需要与服务器协商验证
      • no-store:不缓存任何响应内容
      • public/private:资源缓存范围控制

协商缓存(Negotiation Cache):

    • Last-Modified/If-Modified-Since:基于资源修改时间的缓存控制
    • ETag/If-None-Match:基于资源内容标识的缓存控制,精确度更高

下图查看交互时序

众所周知,强制缓存,如果设置不当就不会更新,如果过于保守则缓存不起作用,又增加了服务器压力,这其中蕴含着系统设计的哲学,那么我们能否既要又要呢?

SWR的出现

因此,仅使用强制缓存header字段很难满足“ 既希望缓存生效,但又尽量提供最新数据”等需求。因此提出的就是 网站图标Stale-While-Revalidate (SwR) 这一 Cache-Control 的扩展。

简单来说,就是“ 先从缓存中显示内容,但会在后台异步更新缓存 ”的机制。

datatracker.ietf.org
HTTP Cache-Control Extensions for Stale Content (RFC 5861, )
网站图标datatracker.ietf.org

假设设置以下缓存控制头

Cache-Control: max-age=60, stale-while-revalidate=3600

这表示:

  • 资源在 60 秒内保持新鲜,浏览器直接使用缓存
  • 60 秒后到 3660 秒内,浏览器仍返回缓存版本,但同时在后台发起请求更新缓存
  • 3660 秒后,浏览器必须等待新请求完成

Stale-While-Revalidate (SWR) 其核心思想是,当资源过期后,缓存会立即返回(stale)的旧数据以保证快速响应,同时在后台(while-revalidate)异步请求新的数据以更新缓存,供后续请求使用。这种策略旨在平衡用户感知的性能和数据的最终一致性。SWR 的主要优势包括:  

  • 提升感知性能:用户能够迅速获得响应,即使数据是旧的,也避免了长时间等待。
  • 减少非关键更新的服务器负载:并非所有数据都需要实时更新,SWR 允许在后台进行更新,减轻了服务器的瞬时压力。
  • 增强用户体验:通过快速加载和后台更新的结合,提供了流畅且数据相对新鲜的用户体验

nextjs 如何利用 SWR 概念的

首先声明,nextjs 只是借助了 SWR 的概念,让 ISR 情况下去降低服务器的压力,

首先来详细梳理一下 Next.js 中 网站图标unstable_cache 的配置项与标准 HTTP 缓存头之间的微妙关系。

首先要明确最重要的一点:

  1. unstable_cache: 主要用于服务器端的数据缓存。它缓存的是函数(通常是数据获取函数,如数据库查询、API 调用)的执行结果。这个缓存存在于 Next.js 服务器(isr)或构建过程(ssg)中,目的是减少重复执行昂贵操作的次数,提高服务器性能和响应速度。
  2. HTTP 缓存头 (Cache-Control, ETag, Last-Modified等): 用于控制客户端(浏览器)和中间缓存(如 CDN)如何缓存整个 HTTP 响应。它们由服务器在 HTTP 响应中发送,告诉下游缓存这个响应可以被缓存多久、如何验证缓存是否仍然有效等。

虽然它们作用于不同层面,但 unstable_cache 的配置(特别是 revalidate)有可能会影响 Next.js 如何决定为包含该缓存数据的页面或路由设置哪些 HTTP 缓存头。

我们来看 unstable_cache 的主要配置项:

其中和 SWR 相关的就是revalidate配置项,它定义了服务器端数据缓存(s-maxage)的有效期(以秒为单位),在此期间,对该缓存函数的调用将直接返回缓存数据,也就是类似浏览器端http 的强缓存,只不过把这个作用在了服务端.

超过时间后,下一次调用会重新执行函数,并将新结果存入缓存(这个过程可以称为 Stale-While-Revalidate)。在重新验证完成前,旧的缓存数据可能仍会被短暂返回。


客户端请求如何使用SWR

相对于 nextjs 可以使用 RSC 融合服务端响应头去使用 SWR 的特性,那单纯客户端请求的时候就很难利用浏览器的特性去控制这个缓存的颗粒度,所以基本上需要自己实现,其实也可以通过好用的第三方客户端请求库去更方便的模拟SWR,下面举两个常见的请求库

reactQuery

tanstack.com
TanStack Query (formerly known as React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes fetching, caching, synchronizing and...
网站图标tanstack.com
预览图片


useSWR

swr.vercel.app
React Hooks for Data Fetching.
网站图标swr.vercel.app

Vercel 的 swr 库本身就是以 Stale-While-Revalidate 命名的,所以它的默认行为就是 SWR。它没有一个直接叫做 staleTime 的选项来定义数据在多久内是 "fresh" 从而阻止自动 revalidation。相反,它通过 revalidateOnFocus, revalidateOnReconnect, refreshInterval 等选项来控制何时触发 "revalidate"。dedupingInterval 则用于防止在短时间内对同一 key 发起重复请求。



如未标记非原创,转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,未经站长允许不得对文章文字内容进行修改演绎,不得用于商业目的。
本文采用CC BY-NC-SA 4.0 - 非商业性使用 - 相同方式共享 4.0 国际进行许可。