前端缓存方式对比表
缓存方案对比表
| 缓存类型 |
存储位置 |
容量限制 |
生命周期 |
是否与服务端交互 |
适用场景 |
|---|
| HTTP缓存 |
浏览器 |
根据浏览器和磁盘空间 |
可配置(Expires/Cache-Control) |
是 |
静态资源缓存、API响应缓存 |
| Cookie |
浏览器 |
4KB左右 |
可设置过期时间 |
每次请求自动携带 |
会话管理、用户标识 |
| LocalStorage |
浏览器 |
5-10MB |
永久存储,除非手动清除 |
否 |
用户偏好设置、应用状态持久化 |
| SessionStorage |
浏览器 |
5-10MB |
标签页/会话期间 |
否 |
表单数据临时保存、单次会话状态 |
| IndexedDB |
浏览器 |
无明确限制(通常50MB+) |
永久存储 |
否 |
结构化数据、离线应用数据存储 |
| Service Worker缓存 |
浏览器/Service Worker |
动态分配(通常50MB+) |
可编程控制 |
可配置 |
离线应用、资源预加载、网络降级处理 |
Service Worker缓存详细讲解
一、Service Worker概述
Service Worker 是一种在浏览器后台运行的脚本,它独立于网页,可以拦截和处理网络请求,实现强大的缓存控制和离线功能。
二、生命周期
// 注册Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW注册成功:', registration.scope);
})
.catch(error => {
console.log('SW注册失败:', error);
});
}
生命周期阶段:
注册 →
安装 →
激活 →
运行 →
闲置 →
终止
三、缓存策略实现
1. 基本缓存结构
// sw.js - Service Worker文件
const CACHE_NAME = 'app-cache-v1';
const STATIC_ASSETS = [
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
];
// 安装阶段 - 预缓存关键资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('缓存已打开');
return cache.addAll(STATIC_ASSETS);
})
.then(() => self.skipWaiting()) // 跳过等待,立即激活
);
});
// 激活阶段 - 清理旧缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME) {
console.log('删除旧缓存:', cacheName);
return caches.delete(cacheName);
}
})
);
}).then(() => self.clients.claim()) // 立即控制所有客户端
);
});
2. 核心缓存策略
// 拦截和处理请求
self.addEventListener('fetch', event => {
event.respondWith(
// 策略选择器
cacheStrategy(event.request)
.catch(() => fallbackStrategy(event.request))
);
});
// 缓存优先策略
async function cacheFirst(request) {
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
try {
const networkResponse = await fetch(request);
// 缓存新资源
const cache = await caches.open(CACHE_NAME);
cache.put(request, networkResponse.clone());
return networkResponse;
} catch (error) {
// 网络失败,返回兜底内容
return new Response('网络连接失败,请检查网络设置', {
status: 408,
headers: { 'Content-Type': 'text/plain' }
});
}
}
// 网络优先策略
async function networkFirst(request) {
try {
const networkResponse = await fetch(request);
// 更新缓存
const cache = await caches.open(CACHE_NAME);
cache.put(request, networkResponse.clone());
return networkResponse;
} catch (error) {
// 网络失败时尝试从缓存获取
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
throw error;
}
}
// Stale-While-Revalidate策略(推荐)
async function staleWhileRevalidate(request) {
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(request);
// 立即返回缓存(无论新旧)
const fetchPromise = fetch(request).then(networkResponse => {
// 更新缓存
cache.put(request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchPromise;
}
// 策略选择器
function cacheStrategy(request) {
const url = new URL(request.url);
// 根据请求类型选择策略
if (request.method !== 'GET') {
return fetch(request); // 非GET请求直接走网络
}
if (url.pathname.startsWith('/api/')) {
// API请求使用网络优先
return networkFirst(request);
}
if (url.pathname.includes('.html') || url.pathname === '/') {
// HTML文件使用网络优先,确保内容最新
return networkFirst(request);
}
if (url.pathname.includes('version=')) {
// 带版本号的资源直接走网络(可能是新版本)
return fetch(request);
}
// 静态资源使用Stale-While-Revalidate
return staleWhileRevalidate(request);
}
// 兜底策略
async function fallbackStrategy(request) {
const url = new URL(request.url);
// 返回离线页面
if (request.headers.get('Accept').includes('text/html')) {
return caches.match('/offline.html');
}
// 返回默认图片
if (request.headers.get('Accept').includes('image')) {
return caches.match('/images/default-placeholder.png');
}
return new Response('资源不可用', {
status: 503,
headers: { 'Content-Type': 'text/plain' }
});
}
四、高级功能
1. 消息通信
// Service Worker发送消息给页面
self.addEventListener('activate', event => {
event.waitUntil(
self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage({
type: 'SW_ACTIVATED',
version: CACHE_NAME
});
});
})
);
});
// 接收页面消息
self.addEventListener('message', event => {
if (event.data.type === 'GET_CACHE_INFO') {
caches.open(CACHE_NAME).then(cache => {
cache.keys().then(keys => {
event.source.postMessage({
type: 'CACHE_INFO',
count: keys.length,
size: '...'
});
});
});
}
if (event.data.type === 'CLEAR_CACHE') {
caches.delete(CACHE_NAME);
}
});
2. 后台同步
// 注册后台同步
self.addEventListener('sync', event => {
if (event.tag === 'sync-user-data') {
event.waitUntil(syncUserData());
}
});
async function syncUserData() {
const requests = await getPendingRequests();
for (const request of requests) {
try {
await fetch(request.url, request.options);
await removePendingRequest(request.id);
} catch (error) {
console.error('同步失败:', error);
}
}
}
3. 推送通知
self.addEventListener('push', event => {
const options = {
body: event.data.text(),
icon: '/images/icon-192.png',
badge: '/images/badge-72.png',
vibrate: [200, 100, 200],
data: {
url: '/notifications'
}
};
event.waitUntil(
self.registration.showNotification('新通知', options)
);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.matchAll({ type: 'window' }).then(clientList => {
// 如果已有窗口打开,聚焦它
for (const client of clientList) {
if (client.url === '/' && 'focus' in client) {
return client.focus();
}
}
// 否则打开新窗口
if (clients.openWindow) {
return clients.openWindow(event.notification.data.url);
}
})
);
});
五、最佳实践
1. 版本控制
// 版本管理
const CACHE_VERSION = 'v2';
const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
// 动态缓存版本检测
async function checkForUpdates() {
const response = await fetch('/version-manifest.json');
const manifest = await response.json();
if (manifest.version !== CACHE_VERSION) {
// 触发Service Worker更新
navigator.serviceWorker.getRegistration().then(reg => {
if (reg) reg.update();
});
}
}
2. 缓存容量管理
// 缓存清理和容量控制
async function manageCacheSize(maxSize = 50 * 1024 * 1024) { // 50MB
const cache = await caches.open(CACHE_NAME);
const keys = await cache.keys();
let totalSize = 0;
const entries = [];
// 计算缓存大小
for (const request of keys) {
const response = await cache.match(request);
if (response) {
const contentLength = response.headers.get('content-length');
const size = contentLength ? parseInt(contentLength) : 0;
totalSize += size;
entries.push({ request, size, timestamp: Date.now() });
}
}
// 如果超过限制,清理最旧的缓存
if (totalSize > maxSize) {
entries.sort((a, b) => a.timestamp - b.timestamp);
let sizeToDelete = 0;
const toDelete = [];
for (const entry of entries) {
if (sizeToDelete >= maxSize * 0.2) break; // 删除20%的缓存
toDelete.push(entry.request);
sizeToDelete += entry.size;
}
await Promise.all(toDelete.map(req => cache.delete(req)));
}
}
3. 性能监控
// 缓存命中率统计
const cacheStats = {
hits: 0,
misses: 0,
errors: 0
};
// 增强的fetch事件处理
self.addEventListener('fetch', event => {
const startTime = performance.now();
event.respondWith(
(async () => {
try {
const response = await cacheStrategy(event.request);
// 统计信息
if (response.headers.get('X-Cache-Status') === 'HIT') {
cacheStats.hits++;
} else {
cacheStats.misses++;
}
// 添加性能监控头
const headers = new Headers(response.headers);
headers.set('X-Response-Time', `${Date.now() - startTime}ms`);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: headers
});
} catch (error) {
cacheStats.errors++;
throw error;
}
})()
);
});
六、调试和测试
// 调试工具
if (process.env.NODE_ENV === 'development') {
// 开发环境特殊处理
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// 跳过开发服务器请求
if (url.hostname === 'localhost' && url.port === '3000') {
return;
}
// 正常的缓存处理
event.respondWith(cacheStrategy(event.request));
});
}
// Service Worker状态检查
function checkServiceWorkerStatus() {
if (!navigator.serviceWorker) {
console.log('当前浏览器不支持Service Worker');
return false;
}
navigator.serviceWorker.getRegistration().then(registration => {
if (registration) {
console.log('Service Worker已注册:', registration.scope);
registration.update(); // 强制更新
} else {
console.log('Service Worker未注册');
}
});
return true;
}
七、注意事项
HTTPS要求:Service Worker 只能在 HTTPS 或 localhost 环境下运行
作用域限制:Service Worker 的作用域由其所在目录决定
缓存清理:用户可能手动清除缓存,需要做好错误处理
内存管理:长时间运行的Service Worker需要注意内存泄漏
更新策略:Service Worker 更新需要谨慎处理,避免破坏用户体验
八、适用场景总结
离线应用:如文档编辑器、阅读器
性能优化:静态资源缓存,减少网络请求
网络降级:弱网环境下的用户体验保障
推送通知:实现后台消息推送
后台同步:网络恢复后的数据同步
通过合理使用Service Worker缓存,可以显著提升Web应用的性能、可靠性和用户体验,特别是在移动网络环境下。