Web缓存策略有哪些?

165 阅读5分钟

Web 缓存策略学习笔记

摘要:浏览器缓存是前端性能优化的核心手段之一。它通过 HTTP 响应头控制资源是否使用本地缓存,分为强缓存(直接用,不请求)、协商缓存(问一下再用)和策略缓存(按场景灵活选择)三大类。理解它们的优先级和触发条件,是掌握 HTTP 缓存的关键。

Web_缓存策略全景图.png

一、整体流程

浏览器请求一个资源时,缓存判断的完整链路:

发起请求
  │
  ▼
┌─────────────┐    命中(未过期)
│  1. 强缓存   │ ──→ 直接使用本地缓存 ✅
│  Expires     │      (状态码 200 from cache)
│  Cache-Control│
└──────┬──────┘
       未命中 / 已过期
       ▼
┌─────────────┐    资源没变
│  2. 协商缓存   │ ──→ 返回 304,继续用旧缓存 ✅
│  Last-Modified│
│  ETag        │
└──────┬──────┘
       资源已变
       ▼
   返回新资源 200 + 更新缓存

二、强缓存

核心思想:不向服务器发请求,直接从本地读取。最快,但可能拿到过期资源。

两个关键响应头

头部示例说明
ExpiresExpires: Wed, 21 Oct 2026 07:28:00 GMT绝对时间,服务器告诉浏览器"这个时间之前别来问我"。缺点:依赖客户端时间,改了系统时间就失效
Cache-ControlCache-Control: max-age=3600相对秒数,距离现在多少秒内有效。优先级高于 Expires,推荐使用

Cache-Control 常用值

Cache-Control: max-age=3600          /* 缓存 1 小时 */
Cache-Control: no-cache              /* 跳过强缓存,走协商缓存 */
Cache-Control: no-store              /* 完全不缓存 */
Cache-control: private               /* 只有浏览器可以缓存,代理/CDN 不行 */
Cache-Control: public                /* 浏览器和代理/CDN 都可以缓存 */
Cache-Control: immutable             /* 资源永不改变(如带 hash 的静态文件)*/

优先级

Cache-Control > Expires

当两者同时存在时,以 Cache-Control 为准。


三、协商缓存

核心思想:强缓存过期后,先问服务器"资源变了没",没变就继续用旧的(返回 304),变了才下载新的。

两对验证字段

类型请求头(浏览器问)响应头(服务器答)原理
Last-ModifiedIf-Modified-SinceLast-Modified对比文件的最后修改时间
ETagIf-None-MatchETag对比文件的唯一标识(哈希值)

完整交互过程

/* 第一次请求(无缓存) */
GET /style.css HTTP/1.1

HTTP/1.1 200 OK
Last-Modified: Mon, 20 Jul 2026 08:00:00 GMT
ETag: "abc123"
Cache-Control: max-age=3600

/* 第二次请求(强缓存已过期,进入协商) */
GET /style.css HTTP/1.1
If-Modified-Since: Mon, 20 Jul 2026 08:00:00 GMT
If-None-Match: "abc123"

/* 服务器判断:资源没变 → 304 */
HTTP/1.1 304 Not Modified
(空 body,节省带宽)

/* 服务器判断:资源变了 → 200 + 新内容 */
HTTP/1.1 200 OK
Last-Modified: Tue, 21 Jul 2026 10:00:00 GMT
ETag: "def456"

Last-Modified vs ETag

维度Last-ModifiedETag
精度秒级,1 秒内修改检测不到精确到文件内容 hash
局限性文件改了但内容不变也会重新下载;不同服务器时间不一致无此问题
优先级较低较高(服务器同时返回两者时,优先用 ETag)

四、策略缓存(Service Worker / Cache API)

核心思想:前两种缓存都由浏览器自动管理,而策略缓存通过 Service Worker 拦截网络请求,由开发者手动决定每个请求怎么处理——读缓存、走网络、还是两者结合。

Service Worker 工作原理

浏览器发起请求
     │
     ▼
 Service Worker (拦截)
     │
     ├── 命中自定义缓存 → 直接返回(离线可用)
     ├── 未命中 → 发起网络请求
     │       │
     │       ├── 成功 → 返回结果 + 存入缓存(下次可直接用)
     │       └── 失败 → 返回缓存的旧版本(降级兜底)
     └── 完全不经过 SW → 正常网络请求

典型策略模式

// 注册 Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

// sw.js 中常见的缓存策略
const CACHE_NAME = 'v1';

// 策略 1:Cache First(缓存优先)—— 静态资源用这个
// 先查缓存,有就直接返回,没有才走网络并缓存
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(CACHE_NAME).then(cache =>
      cache.match(event.request).then(response => {
        return response || fetch(event.request).then(netResponse => {
          cache.put(event.request, netResponse.clone());
          return netResponse;
        });
      })
    )
  );
});

// 策略 2:Network First(网络优先)—— API 数据用这个
// 先走网络,失败时回退到缓存(保证数据新鲜度)
event.respondWith(
  fetch(event.request).catch(() => caches.match(event.request))
);

// 策略 3:Stale While Revalidate(过期但先返回旧版,后台更新)
// 先返回缓存,同时在后台发请求更新缓存
event.respondWith(
  caches.open(CACHE_NAME).then(cache =>
    cache.match(event.request).then(cached => {
      const fetched = fetch(event.request).then(response => {
        cache.put(event.request, response.clone());
        return response;
      });
      return cached || fetched;
    })
  )
);

适用场景

场景推荐策略说明
HTML 入口文件Network First保证拿到最新页面
JS/CSS/图片等静态资源Cache First 或 Stale While Revalidate变更频率低,优先速度
API 接口数据Network First数据需要实时性
离线应用Cache First + 预缓存断网也能用

五、三种缓存对比总结

维度强缓存协商缓存策略缓存 (SW)
是否发请求不发发(带验证头)可控制
状态码200 from cache304 Not Modified由开发者决定
控制方浏览器自动浏览器自动开发者手动
核心字段Expires / Cache-ControlLast-Modified / ETagCache API
离线支持有(未过期时)无完全支持
灵活性低中最高

实际项目中的最佳实践

HTML 文件:
  Cache-Control: no-cache          (每次都协商,确保拿最新入口)

JS/CSS/图片(带 hash):
  Cache-Control: max-age=31536000, immutable   (永久缓存,hash 变了 URL 就变了)

JS/CSS/图片(不带 hash):
  Cache-Control: max-age=86400      (缓存 1 天,过期后协商)

API 接口:
  Cache-Control: no-cache           (不缓存,或用 SW 做 Network First)

六、快速记忆口诀

  • 强缓存:时间没过直接用,max-age 比 Expires 强
  • 协商缓存:过期了问服务器,ETag 比 Last-Modified 准
  • 策略缓存:Service Worker 拦截请求,想存就存、想取就取