浏览器缓存机制
缓存概述
浏览器缓存是提升性能的重要手段,主要包括:
- HTTP 缓存:强缓存、协商缓存
- 浏览器存储:Cookie、LocalStorage、SessionStorage、IndexedDB
- Service Worker 缓存:可编程缓存
HTTP 缓存
1. 强缓存
Cache-Control:
Cache-Control: max-age=3600
Cache-Control: public, max-age=3600
Cache-Control: private, max-age=3600
Cache-Control: no-cache
Cache-Control: no-store
Cache-Control: must-revalidate
常用指令:
max-age:缓存有效期(秒)public:可以被任何缓存存储private:只能被浏览器缓存no-cache:需要验证缓存no-store:不缓存must-revalidate:过期后必须验证immutable:资源不会改变
Expires:
Expires: Wed, 21 Oct 2025 07:28:00 GMT
优先级: Cache-Control > Expires
流程:
1. 检查 Cache-Control/Expires
2. 未过期 → 使用缓存(200 from cache)
3. 已过期 → 进入协商缓存
2. 协商缓存
ETag / If-None-Match:
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified / If-Modified-Since:
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
优先级: ETag > Last-Modified
流程:
1. 发送请求,携带 If-None-Match / If-Modified-Since
2. 服务器比较
3. 未改变 → 304 Not Modified(使用缓存)
4. 已改变 → 200 OK(返回新资源)
3. 缓存策略
静态资源(长期缓存):
Cache-Control: public, max-age=31536000, immutable
HTML(不缓存或短缓存):
Cache-Control: no-cache
# 或
Cache-Control: max-age=0, must-revalidate
API 响应(短缓存):
Cache-Control: private, max-age=300
浏览器存储
1. Cookie
特点:
- 大小限制:4KB
- 每次请求都会携带
- 可以设置过期时间
- 可以设置域名、路径
使用:
// 设置
document.cookie = "name=value; expires=Wed, 21 Oct 2025 07:28:00 GMT; path=/";
// 读取
const cookies = document.cookie.split(';');
// 删除(设置过期时间为过去)
document.cookie = "name=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
属性:
expires:过期时间max-age:有效期(秒)domain:域名path:路径secure:只在 HTTPS 下传输httpOnly:禁止 JS 访问(防 XSS)sameSite:跨站请求限制
2. LocalStorage
特点:
- 大小限制:5-10MB
- 持久化存储
- 同源策略
- 同步 API
使用:
// 设置
localStorage.setItem('key', 'value');
// 读取
const value = localStorage.getItem('key');
// 删除
localStorage.removeItem('key');
// 清空
localStorage.clear();
// 获取所有 key
const keys = Object.keys(localStorage);
事件:
window.addEventListener('storage', (e) => {
console.log(e.key); // 改变的 key
console.log(e.oldValue); // 旧值
console.log(e.newValue); // 新值
console.log(e.url); // 改变的页面 URL
});
3. SessionStorage
特点:
- 大小限制:5-10MB
- 会话级别存储
- 关闭标签页清除
- 同源策略
使用:
// 与 LocalStorage API 相同
sessionStorage.setItem('key', 'value');
sessionStorage.getItem('key');
sessionStorage.removeItem('key');
sessionStorage.clear();
区别:
- LocalStorage:持久化,关闭浏览器不消失
- SessionStorage:会话级别,关闭标签页清除
4. IndexedDB
特点:
- 大小限制:较大(通常 50MB+)
- 异步 API
- 支持复杂数据结构
- 支持事务
使用:
// 打开数据库
const request = indexedDB.open('myDB', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
const objectStore = db.createObjectStore('users', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: false });
};
request.onsuccess = (event) => {
const db = event.target.result;
// 添加数据
const transaction = db.transaction(['users'], 'readwrite');
const objectStore = transaction.objectStore('users');
objectStore.add({ id: 1, name: 'John' });
// 读取数据
const getRequest = objectStore.get(1);
getRequest.onsuccess = (event) => {
console.log(event.target.result);
};
};
Service Worker 缓存
缓存策略
Cache First:
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request).then(response => {
const responseClone = response.clone();
caches.open('v1').then(cache => {
cache.put(event.request, responseClone);
});
return response;
});
})
);
});
Network First:
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request).catch(() => {
return caches.match(event.request);
})
);
});
Stale While Revalidate:
self.addEventListener('fetch', event => {
event.respondWith(
caches.open('v1').then(cache => {
return cache.match(event.request).then(response => {
const fetchPromise = fetch(event.request).then(networkResponse => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return response || fetchPromise;
});
})
);
});
缓存最佳实践
1. 选择合适的存储方式
| 数据类型 | 推荐存储 | 原因 |
|---|---|---|
| 用户登录信息 | Cookie(httpOnly) | 安全性高 |
| 用户偏好设置 | LocalStorage | 持久化 |
| 临时数据 | SessionStorage | 会话级别 |
| 大量结构化数据 | IndexedDB | 容量大、支持查询 |
| 静态资源 | HTTP 缓存 | 性能好 |
| 离线资源 | Service Worker | 可编程 |
2. 缓存版本控制
文件名版本号:
// webpack
output: {
filename: '[name].[contenthash:8].js',
}
查询参数版本号:
<link rel="stylesheet" href="style.css?v=1.0.0" />
3. 缓存更新策略
强制更新:
// 添加版本号或时间戳
const url = `/api/data?v=${Date.now()}`;
Service Worker 更新:
navigator.serviceWorker.register('/sw.js').then(registration => {
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// 新版本可用
}
});
});
});
常见问题
1. 缓存不生效
原因:
- Cache-Control 配置错误
- 文件名没有版本号
- 浏览器禁用缓存
解决:
- 检查 HTTP 响应头
- 使用版本号或 hash
- 清除浏览器缓存测试
2. Cookie 安全问题
最佳实践:
// 设置安全 Cookie
document.cookie = "name=value; secure; httpOnly; sameSite=strict";
3. 存储空间限制
处理:
- 限制存储大小
- 定期清理过期数据
- 使用 LRU 策略
4. 跨域存储
限制:
- 同源策略限制
- 无法跨域访问
解决:
- 使用 postMessage 通信
- 使用 SharedArrayBuffer(需要特殊配置)