浏览器缓存机制
浏览器缓存是性能优化中最重要的环节之一。好的缓存策略可以显著减少网络传输,加快页面加载速度。
1. 缓存位置
缓存查找是有优先级的,顺序如下:
- Service Worker Cache:由开发者编写 JS 控制,自由度最高。
- Memory Cache:内存缓存。
- 读取快,持续时间短(关闭 Tab 即失效)。
- 通常包含图片、脚本等资源。
- Disk Cache:硬盘缓存。
- 读取较慢,容量大,持久存储。
- 通常包含 CSS 等大文件。
- Push Cache:HTTP/2 推送缓存(只存在于 Session 中)。
- 网络请求:如果没有缓存,则发起请求。
2. 缓存策略
浏览器的 HTTP 缓存策略主要分为两类:强缓存和协商缓存。
2.1 强缓存 (Strong Cache)
定义:浏览器直接判断本地缓存是否过期。如果未过期,直接使用,不发送请求到服务器。 状态码:200 OK (from memory cache / from disk cache)
主要通过 HTTP Header 控制:
Expires(HTTP/1.0)- 值为服务器的绝对时间(如
Wed, 22 Oct 2026 08:41:00 GMT)。 - 缺点:如果客户端时间被修改,缓存可能判断错误。
- 值为服务器的绝对时间(如
Cache-Control(HTTP/1.1) - 优先级更高max-age=3600:在 3600 秒内有效(相对时间,解决了 Expires 的问题)。no-cache:不使用强缓存,必须去服务器验证(走协商缓存)。no-store:禁止任何缓存(包括协商缓存),每次都下载最新资源。public/private:是否允许代理服务器(CDN)缓存。
2.2 协商缓存 (Negotiation Cache)
定义:浏览器发现强缓存失效(或设置了 no-cache),则发送请求给服务器,询问资源是否更新。
- 如果未更新:服务器返回 304 Not Modified,浏览器使用本地缓存。
- 如果已更新:服务器返回 200 OK 和新资源。
主要通过两对 Header 控制:
Last-Modified/If-Modified-Since(HTTP/1.0)- Response:
Last-Modified(资源最后修改时间)。 - Request:
If-Modified-Since(下次请求带上上次的时间)。 - 缺点:
- 精度只到秒级。
- 如果文件内容没变但修改时间变了,也会重新下载。
- Response:
ETag/If-None-Match(HTTP/1.1) - 优先级更高- Response:
ETag(文件内容的指纹/哈希值,如Hash-123456)。 - Request:
If-None-Match(下次请求带上上次的 ETag)。 - 优点:精度高,只有内容变了 ETag 才会变。
- 缺点:服务器计算 ETag 需要耗费性能。
- Response:
3. 缓存流程决策图
mermaid
graph TD
Start[发起请求] --> SW{Service Worker 命中?}
SW --Yes--> ReturnSW[返回 SW 缓存]
SW --No--> Strong{强缓存有效?}
Strong --Yes (Cache-Control: max-age)--> ReturnDisk[读取本地缓存 (200)]
Strong --No (过期或 no-cache)--> Negotiate[发送请求到服务器 带上 Etag/Last-Modified]
Negotiate --> ServerCheck{服务器检查}
ServerCheck --未改变 (304)--> ReadLocal[读取本地缓存 更新 Header]
ServerCheck --已改变 (200)--> Download[下载新资源 更新缓存]4. 最佳实践
HTML 文件:
- 使用协商缓存:
Cache-Control: no-cache。 - 确保用户每次打开页面都能获取最新的 HTML(可能包含新的 CSS/JS 文件名)。
- 使用协商缓存:
CSS / JS / 图片等静态资源:
- 使用强缓存:
Cache-Control: max-age=31536000(1年)。 - 配合 Hash 文件名(如
style.a1b2c3.css)。 - 原理:当内容修改时,文件名发生变化(HTML 引用也随之变化),浏览器自然会请求新文件;内容不变时,直接命中强缓存。
- 使用强缓存:
5. Service Worker 缓存
Service Worker 是运行在浏览器后台的脚本,可以拦截网络请求,实现离线体验。
javascript
// 注册 Service Worker
navigator.serviceWorker.register('/sw.js');
// sw.js
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
// 如果命中缓存,直接返回缓存
if (response) {
return response;
}
// 否则发起网络请求
return fetch(event.request);
})
);
});Service Worker 提供了比 HTTP 缓存更细粒度的控制能力,可以实现 Cache First, Network First, Stale While Revalidate 等多种策略。