Web缓存策略有哪些?

Web 缓存策略学习笔记

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

一、整体流程

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

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

二、强缓存

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

两个关键响应头

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

Cache-Control 常用值

http 复制代码
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-Modified If-Modified-Since Last-Modified 对比文件的最后修改时间
ETag If-None-Match ETag 对比文件的唯一标识(哈希值)

完整交互过程

http 复制代码
/* 第一次请求(无缓存) */
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-Modified ETag
精度 秒级,1 秒内修改检测不到 精确到文件内容 hash
局限性 文件改了但内容不变也会重新下载;不同服务器时间不一致 无此问题
优先级 较低 较高(服务器同时返回两者时,优先用 ETag)

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

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

Service Worker 工作原理

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

典型策略模式

js 复制代码
// 注册 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 cache 304 Not Modified 由开发者决定
控制方 浏览器自动 浏览器自动 开发者手动
核心字段 Expires / Cache-Control Last-Modified / ETag Cache API
离线支持 有(未过期时) 完全支持
灵活性 最高

实际项目中的最佳实践

yaml 复制代码
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-ageExpires
  • 协商缓存 :过期了问服务器,ETagLast-Modified
  • 策略缓存:Service Worker 拦截请求,想存就存、想取就取
相关推荐
光影少年8 小时前
react navite性能优化 & 常见坑
前端·react native·掘金·金石计划
做前端的娜娜子4 天前
文档加载工程:多格式数据接入与 Document 标准化
langchain·ai编程·掘金·金石计划
做前端的娜娜子4 天前
Embedding 向量模型:从语义表示到相似度计算
langchain·openai·掘金·金石计划
做前端的娜娜子5 天前
第二个 AI 调用——invoke 阻塞式与 stream 流式生成
人工智能·llm·掘金·金石计划
做前端的娜娜子6 天前
第一个AI调用——TypeScript 工程初始化与配置管理(实战跟练)
langchain·openai·掘金·金石计划
做前端的娜娜子7 天前
什么是浅拷贝(Shallow Copy)和深拷贝(Deep Copy)?如何手动实现一个深拷贝函数
javascript·面试·掘金·金石计划
做前端的娜娜子9 天前
JavaScript 闭包
前端·javascript·掘金·金石计划
做前端的娜娜子10 天前
移动端上拉加载与下拉刷新实现方案
前端·面试·掘金·金石计划
做前端的娜娜子14 天前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
做前端的娜娜子18 天前
如何实现网页加载进度条?
性能优化·掘金·金石计划