前端缓存方式对比表
| 缓存方式 | 工作机制 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| HTTP缓存 | 通过HTTP头控制缓存策略 | 1. 浏览器自动管理 2. 减少网络请求 3. 强缓存零延迟 | 1. 需服务器配置 2. 缓存更新需策略控制 | 静态资源(CSS/JS/图片) |
| • 强缓存 (Expires/Cache-Control) | 直接使用本地副本,不发请求 | 响应最快,零网络开销 | 更新不及时,需版本控制 | 版本化静态资源 |
| • 协商缓存 (Last-Modified/Etag) | 向服务器验证资源是否更新 | 保证资源最新 节省带宽 | 仍有请求延迟 需服务器计算标识 | 频繁更新的资源 |
| Service Worker缓存 | 拦截请求,可编程控制缓存 | 1. 离线可用 2. 细粒度控制 3. 可结合多种策略 | 1. 实现复杂 2. 需HTTPS 3. 首次需注册 | PWA、离线应用、复杂缓存策略 |
| Web Storage (Local/Session Storage) | 键值对存储,同源策略 | 1. 存储量大(5-10MB) 2. API简单 3. 同标签页共享 | 1. 同步操作 2. 仅字符串 3. 无自动过期 | 用户偏好、表单数据、非敏感信息 |
| IndexedDB | 浏览器中的NoSQL数据库 | 1. 大容量(>250MB) 2. 事务支持 3. 复杂查询 | 1. API复杂 2. 异步操作 3. 学习成本高 | 大型结构化数据、离线数据 |
| Cookie | 随请求自动发送到服务器 | 1. 自动管理 2. 服务端可读 3. 有过期机制 | 1. 容量小(4KB) 2. 性能开销 3. 安全性需注意 | 会话管理、身份认证 |
| 应用缓存 (Application Cache) | 通过manifest文件声明缓存 | 1. 简单配置 2. 离线可用 | 1. 已废弃 2. 更新机制差 3. 调试困难 | 不推荐使用,用Service Worker替代 |
📊 缓存策略选择建议
按资源类型选择:
-
静态资源(版本固定):强缓存 + 文件名哈希
-
静态资源(频繁更新):协商缓存
-
API数据:Service Worker + 网络优先/缓存备用策略
-
用户数据:Web Storage(小量)、IndexedDB(大量)
-
会话数据:Session Storage / Cookie
性能优化组合:
-
静态资源 :
Cache-Control: max-age=31536000(一年)+ 文件名哈希 -
动态内容 :
Cache-Control: no-cache+ ETag验证 -
离线应用:Service Worker(Cache API + 回退策略)
-
状态持久化:Redux/Pinia + Web Storage持久化插件
⚠️ 注意事项:
-
缓存污染:合理设置缓存键,避免数据混淆
-
内存管理:定期清理过期缓存(特别是Cache API)
-
安全:敏感数据避免存储在LocalStorage
-
版本控制:静态资源使用内容哈希,避免更新问题
🔄 缓存更新策略示例
javascript
javascript
// Service Worker 缓存策略:网络优先,失败用缓存
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
.catch(() => caches.match(event.request))
);
});
// 版本化缓存:资源URL带哈希
// style.abc123.css -> 强缓存一年
<link href="/css/style.abc123.css" rel="stylesheet">
选择合适的缓存策略需要综合考虑资源类型 、更新频率 、数据大小 和用户体验要求,通常需要组合使用多种缓存方式。
Service Worker 缓存详解
一、Service Worker 基础架构
1. 生命周期
注册 → 安装 → 激活 → 监听事件 → 终止
2. 核心特性
-
独立线程:与主线程分离,不阻塞UI
-
可编程缓存:通过Cache API精细控制
-
请求拦截:可拦截、修改网络请求
-
离线支持:网络不可用时仍可响应
-
后台同步:支持后台数据同步
二、Cache API 详解
1. 缓存存储结构
javascript
// 打开/创建缓存
caches.open('v1').then(cache => {
// cache 对象操作
});
// 查看所有缓存
caches.keys().then(keys => {
console.log('所有缓存:', keys);
});
2. 缓存操作方法
javascript
const CACHE_NAME = 'my-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
];
// 1. 添加单个资源
cache.put(request, response);
// 2. 添加多个资源
cache.addAll(urlsToCache);
// 3. 匹配缓存
cache.match(request);
// 4. 删除缓存
cache.delete(request);
// 5. 获取所有缓存键
cache.keys();
三、常用缓存策略
1. 缓存优先(Cache First)
javascript
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 有缓存直接返回
if (response) {
return response;
}
// 否则从网络获取
return fetch(event.request)
.then(response => {
// 可选:缓存新资源
return caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
2. 网络优先(Network First)
javascript
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
.then(response => {
// 网络成功:更新缓存
return caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, response.clone());
return response;
});
})
.catch(() => {
// 网络失败:使用缓存
return caches.match(event.request);
})
);
});
3. 仅缓存(Cache Only)
javascript
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (!response) {
// 没有缓存时返回兜底页面
return caches.match('/offline.html');
}
return response;
})
);
});
4. 仅网络(Network Only)
javascript
self.addEventListener('fetch', event => {
event.respondWith(fetch(event.request));
});
5. Stale-While-Revalidate(先旧后新)
javascript
self.addEventListener('fetch', event => {
event.respondWith(
caches.open(CACHE_NAME).then(cache => {
return cache.match(event.request).then(cachedResponse => {
const fetchPromise = fetch(event.request).then(networkResponse => {
// 更新缓存
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
// 优先返回缓存,同时更新缓存
return cachedResponse || fetchPromise;
});
})
);
});
6. 运行时缓存(Runtime Caching)
javascript
// 动态决定缓存策略
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// API请求:网络优先
if (url.pathname.startsWith('/api/')) {
return networkFirstStrategy(event);
}
// 静态资源:缓存优先
if (url.pathname.startsWith('/static/')) {
return cacheFirstStrategy(event);
}
// 其他:默认策略
return defaultStrategy(event);
});
四、高级缓存模式
1. 版本化缓存管理
javascript
const CACHE_VERSION = 'v2';
const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
const OLD_CACHES = ['app-cache-v1'];
// 安装时:缓存关键资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(criticalResources))
.then(() => self.skipWaiting()) // 立即激活
);
});
// 激活时:清理旧缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (OLD_CACHES.includes(cacheName)) {
return caches.delete(cacheName);
}
})
);
}).then(() => self.clients.claim()) // 控制所有客户端
);
});
2. 缓存过期策略
javascript
// 定期清理过期缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
return cache.keys().then(requests => {
return Promise.all(
requests.map(request => {
return cache.match(request).then(response => {
if (!response) return null;
// 检查缓存时间
const cachedDate = new Date(response.headers.get('date'));
const now = new Date();
const cacheAge = now - cachedDate;
// 超过7天删除
if (cacheAge > 7 * 24 * 60 * 60 * 1000) {
return cache.delete(request);
}
return null;
});
})
);
});
})
);
});
3. 请求预缓存
javascript
// 预缓存可能访问的资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
// 缓存当前页面资源
return cache.addAll(currentPageResources);
}).then(() => {
// 预加载其他页面资源(不阻塞安装)
self.skipWaiting();
return preloadFutureResources();
})
);
});
function preloadFutureResources() {
return fetch('/api/predict-resources')
.then(res => res.json())
.then(resources => {
return caches.open(CACHE_NAME)
.then(cache => cache.addAll(resources));
})
.catch(err => console.log('预加载失败:', err));
}
4. 响应定制与降级
javascript
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) return response;
return fetch(event.request)
.then(networkResponse => {
// 只缓存成功的响应
if (networkResponse.ok) {
const clonedResponse = networkResponse.clone();
caches.open(CACHE_NAME)
.then(cache => cache.put(event.request, clonedResponse));
}
return networkResponse;
})
.catch(error => {
// 根据请求类型返回不同降级内容
const request = event.request;
if (request.headers.get('Accept').includes('text/html')) {
return caches.match('/offline.html');
}
if (request.headers.get('Accept').includes('image')) {
return caches.match('/images/placeholder.jpg');
}
return new Response('网络不可用', {
status: 503,
headers: { 'Content-Type': 'text/plain' }
});
});
})
);
});
5. 智能缓存策略
javascript
class SmartCache {
constructor() {
this.config = {
maxSize: 50 * 1024 * 1024, // 50MB
ttl: {
html: 5 * 60 * 1000, // 5分钟
api: 60 * 1000, // 1分钟
image: 7 * 24 * 60 * 60 * 1000 // 7天
}
};
}
async shouldCache(request, response) {
const url = new URL(request.url);
// 不缓存非GET请求
if (request.method !== 'GET') return false;
// 不缓存API错误响应
if (!response.ok) return false;
// 不缓存大文件(>10MB)
const contentLength = response.headers.get('content-length');
if (contentLength > 10 * 1024 * 1024) return false;
return true;
}
}
五、性能优化技巧
1. 缓存分层策略
javascript
// 多级缓存:内存 + Service Worker + HTTP
class LayeredCache {
constructor() {
this.cacheLevels = {
memory: new Map(), // 内存缓存(会话级)
sw: CACHE_NAME, // Service Worker缓存
http: null // HTTP缓存(浏览器控制)
};
}
async get(request) {
// 1. 先查内存缓存
const memoryKey = request.url;
if (this.cacheLevels.memory.has(memoryKey)) {
return this.cacheLevels.memory.get(memoryKey);
}
// 2. 再查Service Worker缓存
const swCache = await caches.open(this.cacheLevels.sw);
const swResponse = await swCache.match(request);
if (swResponse) {
// 存入内存缓存
this.cacheLevels.memory.set(memoryKey, swResponse.clone());
return swResponse;
}
// 3. 最后走网络(HTTP缓存会生效)
return fetch(request);
}
}
2. 缓存预热
javascript
// 空闲时预加载资源
self.addEventListener('message', event => {
if (event.data.type === 'PRELOAD_RESOURCES') {
event.waitUntil(
preloadResources(event.data.urls)
);
}
});
function preloadResources(urls) {
return Promise.all(
urls.map(url => {
return fetch(url)
.then(response => {
if (response.ok) {
return caches.open(CACHE_NAME)
.then(cache => cache.put(url, response));
}
})
.catch(() => {/* 静默失败 */});
})
);
}
六、调试与监控
1. 调试工具
javascript
// Service Worker调试日志
self.addEventListener('fetch', event => {
console.log(`[SW] Fetch: ${event.request.url}`);
console.log(`[SW] Mode: ${event.request.mode}`);
console.log(`[SW] Cache: ${caches.keys()}`);
});
2. 缓存状态监控
javascript
// 监控缓存使用情况
async function getCacheStats() {
const cache = await caches.open(CACHE_NAME);
const requests = await cache.keys();
let totalSize = 0;
const stats = await Promise.all(
requests.map(async request => {
const response = await cache.match(request);
const contentLength = response.headers.get('content-length');
return {
url: request.url,
size: parseInt(contentLength || 0),
type: response.headers.get('content-type')
};
})
);
totalSize = stats.reduce((sum, item) => sum + item.size, 0);
return {
totalItems: stats.length,
totalSize,
items: stats
};
}
七、最佳实践
✅ 推荐做法
-
资源分类:不同资源类型使用不同策略
-
版本控制:每次更新使用新缓存名称
-
渐进增强:缓存作为性能优化,不是必需功能
-
监控告警:监控缓存命中率和错误率
-
容量控制:定期清理过期缓存
⚠️ 注意事项
-
HTTPS要求:生产环境必须使用HTTPS
-
缓存污染:避免缓存个性化内容
-
内存泄漏:定期清理无用缓存
-
更新策略:明确缓存失效机制
-
回退方案:网络和缓存都失败时的处理
📊 策略选择指南
| 场景 | 推荐策略 | 理由 |
|---|---|---|
| 静态资源(版本化) | 缓存优先 + 长期缓存 | 内容不变,性能最优 |
| API数据(实时性高) | 网络优先 | 保证数据最新 |
| 用户生成内容 | Stale-While-Revalidate | 平衡速度与新鲜度 |
| 离线应用 | 缓存优先 + 降级页面 | 保证基本功能可用 |
| 大文件(视频/图片) | 按需缓存 + 容量控制 | 避免存储空间占用过大 |
Service Worker缓存是现代Web应用性能优化的核心工具,合理使用可以显著提升用户体验,特别是在弱网和离线场景下。