2、浏览器缓存机制:强缓存与协商缓存、Cache-Control与Expires、ETag与Last-Modified
聊图片复用,绕不开浏览器缓存。说白了,缓存就是浏览器帮你省流量的「记忆体」。你想想看,用户从页面A跳到页面B,如果同一张图片每次都要重新请求,那体验得多糟糕。我早期做项目时,就吃过不懂缓存的亏——明明图片没变,却让用户白白浪费流量。
浏览器缓存分两种:强缓存和协商缓存。它们不是二选一的关系,而是配合使用的。先看强缓存,它最霸道——只要没过期,连请求都不发。
2.1 强缓存:浏览器说了算
强缓存的核心逻辑很简单:浏览器判断资源还在有效期内,就直接从本地读取,HTTP状态码返回200(from disk cache 或 from memory cache)。整个过程不跟服务器打招呼。
控制强缓存有两个老牌字段:Expires 和 Cache-Control。
2.1.1 Expires:HTTP/1.0 的遗产
Expires 是一个绝对时间戳,比如:
Expires: Thu, 31 Dec 2025 23:59:59 GMT
浏览器拿到这个时间,跟本地时钟对比。没到?用缓存。到了?发请求。
2.1.2 Cache-Control:现代标准
Cache-Control 是 HTTP/1.1 引入的,用相对时间控制缓存。常用指令:
| 指令 | 含义 | 示例 |
|---|---|---|
max-age |
资源可缓存的最大秒数 | max-age=31536000 |
no-cache |
强制走协商缓存(不是不缓存) | no-cache |
no-store |
完全不缓存 | no-store |
public |
任何中间节点都可缓存 | public, max-age=86400 |
private |
仅浏览器可缓存 | private, max-age=86400 |
我个人习惯:能上 Cache-Control 就别用 Expires。如果非要兼容老系统,两个一起写——Cache-Control 优先级更高。
max-age=31536000(一年)。配合文件名哈希(比如 logo.a1b2c3d4.png),既能长期缓存,又能精确更新。
2.2 协商缓存:跟服务器商量一下
强缓存过期了,不代表资源一定变了。这时候轮到协商缓存出场。浏览器带着「凭证」去问服务器:这个资源还新鲜吗?服务器说「没变」,返回304状态码,浏览器继续用缓存。
协商缓存有两组搭档:Last-Modified / If-Modified-Since 和 ETag / If-None-Match。
2.2.1 Last-Modified / If-Modified-Since
服务器返回资源时,带上最后修改时间:
Last-Modified: Wed, 15 Mar 2025 10:30:00 GMT
浏览器下次请求时,带上这个时间:
If-Modified-Since: Wed, 15 Mar 2025 10:30:00 GMT
服务器对比时间。没变?返回304。变了?返回200+新资源。
2.2.2 ETag / If-None-Match
ETag 是资源的唯一标识符,通常由文件内容哈希生成。它比时间戳更精确:
ETag: "abc123def456"
浏览器下次请求时带上:
If-None-Match: "abc123def456"
服务器对比ETag。匹配?304。不匹配?200+新资源。
我建议:ETag 优先于 Last-Modified。如果服务器同时返回两者,浏览器会同时使用——ETag 的优先级更高。
2.3 缓存决策流程
浏览器拿到一个图片资源,到底走哪条路?我画了一张流程图,帮你理清逻辑:
2.4 图片缓存的实战配置
回到我们的课程主题——图片资源在页面切换中的复用。我推荐一套经过验证的配置方案:
| 场景 | Cache-Control | 协商缓存 | 说明 |
|---|---|---|---|
| 静态图片(logo、图标) | public, max-age=31536000, immutable |
ETag(内容哈希) | 一年不换,文件名带哈希 |
| 用户头像 | private, max-age=86400 |
ETag + Last-Modified | 一天缓存,用户专属 |
| 轮播图(可能更新) | public, max-age=3600 |
ETag | 一小时强缓存,之后协商 |
| 背景大图 | public, max-age=604800 |
Last-Modified | 一周缓存,节省带宽 |
Cache-Control 中加入 immutable 指令(Chrome 和 Firefox 支持),告诉浏览器:这个资源在过期前绝对不会变。这样即使用户手动刷新页面,浏览器也不会发起协商缓存请求。
2.5 常见问题与避坑
做图片缓存,有几个坑我踩过,分享给你:
- 缓存更新不及时:用户看到的还是旧图片。解决方案:文件名加版本哈希,比如
banner.v2.png。不要只依赖缓存过期。 - CDN 缓存不一致:不同节点缓存不同版本。解决方案:统一 ETag 生成策略,确保所有节点对同一资源返回相同 ETag。
- HTTPS 与缓存代理:某些代理服务器会剥离
Cache-Control。解决方案:在响应头中同时保留Expires作为降级方案。 - Service Worker 干扰:如果用了 Service Worker,它可能接管缓存策略。解决方案:明确 SW 中的缓存逻辑,避免与 HTTP 缓存冲突。