前端缓存方式对比表和Service Worker缓存详细讲解
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">前端缓存方式对比表</a></li><li><a href="#_label1">缓存策略选择建议</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">按资源类型选择:</a></li><li><a href="#_lab2_1_1">性能优化组合:</a></li><li><a href="#_lab2_1_2">注意事项:</a></li><li><a href="#_lab2_1_3">缓存更新策略示例</a></li></ul><li><a href="#_label2">Service Worker 缓存详解</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_4">一、Service Worker 基础架构</a></li><ul class="third_class_ul"><li><a href="#_label3_2_4_0">1.生命周期</a></li><li><a href="#_label3_2_4_1">2.核心特性</a></li></ul><li><a href="#_lab2_2_5">二、Cache API 详解</a></li><ul class="third_class_ul"><li><a href="#_label3_2_5_2">1.缓存存储结构</a></li><li><a href="#_label3_2_5_3">2.缓存操作方法</a></li></ul><li><a href="#_lab2_2_6">三、常用缓存策略</a></li><ul class="third_class_ul"><li><a href="#_label3_2_6_4">1.缓存优先(Cache First)</a></li><li><a href="#_label3_2_6_5">2.网络优先(Network First)</a></li><li><a href="#_label3_2_6_6">3.仅缓存(Cache Only)</a></li><li><a href="#_label3_2_6_7">4.仅网络(Network Only)</a></li><li><a href="#_label3_2_6_8">5.Stale-While-Revalidate(先旧后新)</a></li><li><a href="#_label3_2_6_9">6.运行时缓存(Runtime Caching)</a></li></ul><li><a href="#_lab2_2_7">四、高级缓存模式</a></li><ul class="third_class_ul"><li><a href="#_label3_2_7_10">1.版本化缓存管理</a></li><li><a href="#_label3_2_7_11">2.缓存过期策略</a></li><li><a href="#_label3_2_7_12">3.请求预缓存</a></li><li><a href="#_label3_2_7_13">4.响应定制与降级</a></li><li><a href="#_label3_2_7_14">5.智能缓存策略</a></li></ul><li><a href="#_lab2_2_8">五、性能优化技巧</a></li><ul class="third_class_ul"><li><a href="#_label3_2_8_15">1.缓存分层策略</a></li><li><a href="#_label3_2_8_16">2.缓存预热</a></li></ul><li><a href="#_lab2_2_9">六、调试与监控</a></li><ul class="third_class_ul"><li><a href="#_label3_2_9_17">1.调试工具</a></li><li><a href="#_label3_2_9_18">2.缓存状态监控</a></li></ul><li><a href="#_lab2_2_10">七、最佳实践</a></li><ul class="third_class_ul"><li><a href="#_label3_2_10_19">推荐做法</a></li><li><a href="#_label3_2_10_20">注意事项</a></li></ul><li><a href="#_lab2_2_11">策略选择指南</a></li><ul class="third_class_ul"></ul></ul><li><a href="#_label3">总结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>前端缓存方式对比表</h2><table><thead><tr><th>缓存方式</th><th>工作机制</th><th>优势</th><th>劣势</th><th>适用场景</th></tr></thead><tbody><tr><td><span><strong>HTTP缓存</strong></span></td><td><span>通过HTTP头控制缓存策略</span></td><td><span>1. 浏览器自动管理<br />2. 减少网络请求<br />3. 强缓存零延迟</span></td><td><span>1. 需服务器配置<br />2. 缓存更新需策略控制</span></td><td><span>静态资源(CSS/JS/图片)</span></td></tr><tr><td>• 强缓存<br />(Expires/Cache-Control)</td><td>直接使用本地副本,不发请求</td><td>响应最快,零网络开销</td><td>更新不及时,需版本控制</td><td>版本化静态资源</td></tr><tr><td>• 协商缓存<br />(Last-Modified/Etag)</td><td>向服务器验证资源是否更新</td><td>保证资源最新<br />节省带宽</td><td>仍有请求延迟<br />需服务器计算标识</td><td>频繁更新的资源</td></tr><tr><td><span><strong>Service Worker缓存</strong></span></td><td><span>拦截请求,可编程控制缓存</span></td><td><span>1. 离线可用<br />2. 细粒度控制<br />3. 可结合多种策略</span></td><td><span>1. 实现复杂<br />2. 需HTTPS<br />3. 首次需注册</span></td><td><span>PWA、离线应用、复杂缓存策略</span></td></tr><tr><td><strong>Web Storage</strong><br />(Local/Session Storage)</td><td>键值对存储,同源策略</td><td>1. 存储量大(5-10MB)<br />2. API简单<br />3. 同标签页共享</td><td>1. 同步操作<br />2. 仅字符串<br />3. 无自动过期</td><td>用户偏好、表单数据、非敏感信息</td></tr><tr><td><strong>IndexedDB</strong></td><td>浏览器中的NoSQL数据库</td><td>1. 大容量(>250MB)<br />2. 事务支持<br />3. 复杂查询</td><td>1. API复杂<br />2. 异步操作<br />3. 学习成本高</td><td>大型结构化数据、离线数据</td></tr><tr><td><strong>Cookie</strong></td><td>随请求自动发送到服务器</td><td>1. 自动管理<br />2. 服务端可读<br />3. 有过期机制</td><td>1. 容量小(4KB)<br />2. 性能开销<br />3. 安全性需注意</td><td>会话管理、身份认证</td></tr><tr><td><strong>应用缓存</strong><br />(Application Cache)</td><td>通过manifest文件声明缓存</td><td>1. 简单配置<br />2. 离线可用</td><td><span>1. <strong>已废弃</strong><br />2. 更新机制差<br />3. 调试困难</span></td><td><span>不推荐使用,用Service Worker替代</span></td></tr></tbody></table>
<p class="maodian"><a name="_label1"></a></p><h2>缓存策略选择建议</h2>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>按资源类型选择:</h3>
<ul><li><p><strong>静态资源(版本固定)</strong>:强缓存 + 文件名哈希</p></li><li><p><strong>静态资源(频繁更新)</strong>:协商缓存</p></li><li><p><strong>API数据</strong>:Service Worker + 网络优先/缓存备用策略</p></li><li><p><strong>用户数据</strong>:Web Storage(小量)、IndexedDB(大量)</p></li><li><p><strong>会话数据</strong>:Session Storage / Cookie</p></li></ul>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>性能优化组合:</h3>
<ol><li><p><strong>静态资源</strong>:<code>Cache-Control: max-age=31536000</code>(一年)+ 文件名哈希</p></li><li><p><strong>动态内容</strong>:<code>Cache-Control: no-cache</code> + ETag验证</p></li><li><p><strong>离线应用</strong>:Service Worker(Cache API + 回退策略)</p></li><li><p><strong>状态持久化</strong>:Redux/Pinia + Web Storage持久化插件</p></li></ol>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>注意事项:</h3>
<ul><li><p><strong>缓存污染</strong>:合理设置缓存键,避免数据混淆</p></li><li><p><strong>内存管理</strong>:定期清理过期缓存(特别是Cache API)</p></li><li><p><strong>安全</strong>:敏感数据避免存储在LocalStorage</p></li><li><p><strong>版本控制</strong>:静态资源使用内容哈希,避免更新问题</p></li></ul>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>缓存更新策略示例</h3>
<p>javascript</p>
<div class="jb51code"><pre class="brush:js;">// 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="external nofollow"rel="stylesheet"></pre></div>
<p>选择合适的缓存策略需要综合考虑<strong>资源类型</strong>、<strong>更新频率</strong>、<strong>数据大小</strong>和<strong>用户体验</strong>要求,通常需要组合使用多种缓存方式。</p>
<p class="maodian"><a name="_label2"></a></p><h2>Service Worker 缓存详解</h2>
<p class="maodian"><a name="_lab2_2_4"></a></p><h3>一、Service Worker 基础架构</h3>
<p class="maodian"><a name="_label3_2_4_0"></a></p><h4>1.生命周期</h4>
<blockquote><p>注册 → 安装 → 激活 → 监听事件 → 终止</p></blockquote>
<p class="maodian"><a name="_label3_2_4_1"></a></p><h4>2.核心特性</h4>
<ul><li><p><strong>独立线程</strong>:与主线程分离,不阻塞UI</p></li><li><p><strong>可编程缓存</strong>:通过Cache API精细控制</p></li><li><p><strong>请求拦截</strong>:可拦截、修改网络请求</p></li><li><p><strong>离线支持</strong>:网络不可用时仍可响应</p></li><li><p><strong>后台同步</strong>:支持后台数据同步</p></li></ul>
<p class="maodian"><a name="_lab2_2_5"></a></p><h3>二、Cache API 详解</h3>
<p class="maodian"><a name="_label3_2_5_2"></a></p><h4>1.缓存存储结构</h4>
<div class="jb51code"><pre class="brush:js;">// 打开/创建缓存
caches.open('v1').then(cache => {
// cache 对象操作
});
// 查看所有缓存
caches.keys().then(keys => {
console.log('所有缓存:', keys);
});</pre></div>
<p class="maodian"><a name="_label3_2_5_3"></a></p><h4>2.缓存操作方法</h4>
<div class="jb51code"><pre class="brush:js;">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();</pre></div>
<p class="maodian"><a name="_lab2_2_6"></a></p><h3>三、常用缓存策略</h3>
<p class="maodian"><a name="_label3_2_6_4"></a></p><h4>1.缓存优先(Cache First)</h4>
<div class="jb51code"><pre class="brush:js;">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;
});
});
})
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_5"></a></p><h4>2.网络优先(Network First)</h4>
<div class="jb51code"><pre class="brush:js;">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);
})
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_6"></a></p><h4>3.仅缓存(Cache Only)</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (!response) {
// 没有缓存时返回兜底页面
return caches.match('/offline.html');
}
return response;
})
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_7"></a></p><h4>4.仅网络(Network Only)</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event => {
event.respondWith(fetch(event.request));
});</pre></div>
<p class="maodian"><a name="_label3_2_6_8"></a></p><h4>5.Stale-While-Revalidate(先旧后新)</h4>
<div class="jb51code"><pre class="brush:js;">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;
});
})
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_9"></a></p><h4>6.运行时缓存(Runtime Caching)</h4>
<div class="jb51code"><pre class="brush:js;">// 动态决定缓存策略
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);
});</pre></div>
<p class="maodian"><a name="_lab2_2_7"></a></p><h3>四、高级缓存模式</h3>
<p class="maodian"><a name="_label3_2_7_10"></a></p><h4>1.版本化缓存管理</h4>
<div class="jb51code"><pre class="brush:js;">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()) // 控制所有客户端
);
});</pre></div>
<p class="maodian"><a name="_label3_2_7_11"></a></p><h4>2.缓存过期策略</h4>
<div class="jb51code"><pre class="brush:js;">// 定期清理过期缓存
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;
});
})
);
});
})
);
});</pre></div>
<p class="maodian"><a name="_label3_2_7_12"></a></p><h4>3.请求预缓存</h4>
<div class="jb51code"><pre class="brush:js;">// 预缓存可能访问的资源
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));
}</pre></div>
<p class="maodian"><a name="_label3_2_7_13"></a></p><h4>4.响应定制与降级</h4>
<div class="jb51code"><pre class="brush:js;">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' }
});
});
})
);
});</pre></div>
<p class="maodian"><a name="_label3_2_7_14"></a></p><h4>5.智能缓存策略</h4>
<div class="jb51code"><pre class="brush:js;">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;
}
}</pre></div>
<p class="maodian"><a name="_lab2_2_8"></a></p><h3>五、性能优化技巧</h3>
<p class="maodian"><a name="_label3_2_8_15"></a></p><h4>1.缓存分层策略</h4>
<div class="jb51code"><pre class="brush:js;">// 多级缓存:内存 + 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);
}
}</pre></div>
<p class="maodian"><a name="_label3_2_8_16"></a></p><h4>2.缓存预热</h4>
<div class="jb51code"><pre class="brush:js;">// 空闲时预加载资源
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(() => {/* 静默失败 */});
})
);
}</pre></div>
<p class="maodian"><a name="_lab2_2_9"></a></p><h3>六、调试与监控</h3>
<p class="maodian"><a name="_label3_2_9_17"></a></p><h4>1.调试工具</h4>
<div class="jb51code"><pre class="brush:js;">// Service Worker调试日志
self.addEventListener('fetch', event => {
console.log(` Fetch: ${event.request.url}`);
console.log(` Mode: ${event.request.mode}`);
console.log(` Cache: ${caches.keys()}`);
});</pre></div>
<p class="maodian"><a name="_label3_2_9_18"></a></p><h4>2.缓存状态监控</h4>
<div class="jb51code"><pre class="brush:js;">// 监控缓存使用情况
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
};
}</pre></div>
<p class="maodian"><a name="_lab2_2_10"></a></p><h3>七、最佳实践</h3>
<p class="maodian"><a name="_label3_2_10_19"></a></p><h4>推荐做法</h4>
<ol><li><p><span><strong>资源分类</strong>:不同资源类型使用不同策略</span></p></li><li><p><span><strong>版本控制</strong>:每次更新使用新缓存名称</span></p></li><li><p><span><strong>渐进增强</strong>:缓存作为性能优化,不是必需功能</span></p></li><li><p><span><strong>监控告警</strong>:监控缓存命中率和错误率</span></p></li><li><p><span><strong>容量控制</strong>:定期清理过期缓存</span></p></li></ol>
<p class="maodian"><a name="_label3_2_10_20"></a></p><h4>注意事项</h4>
<ol><li><p><strong>HTTPS要求</strong>:生产环境必须使用HTTPS</p></li><li><p><span><strong>缓存污染</strong>:避免缓存个性化内容</span></p></li><li><p><strong>内存泄漏</strong>:定期清理无用缓存</p></li><li><p><strong>更新策略</strong>:明确缓存失效机制</p></li><li><p><strong>回退方案</strong>:网络和缓存都失败时的处理</p></li></ol>
<p class="maodian"><a name="_lab2_2_11"></a></p><h3>策略选择指南</h3>
<table><thead><tr><th><span>场景</span></th><th>推荐策略</th><th>理由</th></tr></thead><tbody><tr><td><span>静态资源(版本化)</span></td><td>缓存优先 + 长期缓存</td><td>内容不变,性能最优</td></tr><tr><td><span>API数据(实时性高)</span></td><td>网络优先</td><td>保证数据最新</td></tr><tr><td><span>用户生成内容</span></td><td>Stale-While-Revalidate</td><td>平衡速度与新鲜度</td></tr><tr><td><span>离线应用</span></td><td>缓存优先 + 降级页面</td><td>保证基本功能可用</td></tr><tr><td><span>大文件(视频/图片)</span></td><td>按需缓存 + 容量控制</td><td>避免存储空间占用过大</td></tr></tbody></table>
<p>Service Worker缓存是现代Web应用性能优化的核心工具,合理使用可以显著提升用户体验,特别是在弱网和离线场景下。</p>
<p class="maodian"><a name="_label3"></a></p><h2>总结</h2>
頁:
[1]