如何使用现代 Web 技术实现离线缓存?Service Worker 详解
现代离线存储的核心:Service Worker 与 Cache API
在构建具备离线能力的 Web 应用时,开发者需要一种能够在无网络环境下依然提供可用性的机制。当前主流解决方案是结合 Service Worker 和 Cache Storage API 实现资源的智能缓存与请求拦截。
注意:早期基于 HTML5 manifest 文件的 Application Cache(AppCache)技术由于设计缺陷已被淘汰,不建议用于新项目。
工作原理概述
Service Worker 是一个可编程的代理层,运行在浏览器后台线程中,独立于主页面执行。它能够监听并控制页面发出的网络请求,通过以下流程实现离线支持:
- 注册与安装:页面加载时注册指定的 Service Worker 脚本,浏览器下载并触发其
install事件。 - 预缓存静态资源:在安装阶段将关键资源(如 HTML、CSS、JS、图片)存入 Cache Storage。
- 激活与清理:新版本 Worker 激活时清除旧缓存,避免冗余数据占用空间。
- 请求拦截与响应:通过监听
fetch事件,优先从缓存返回内容;若未命中,则发起真实请求并动态缓存结果。
实现步骤
1. 编写 Service Worker 脚本(例如 service-worker.js)
// service-worker.js
const CACHE_VERSION = 'static-v2';
const PRECACHE_LIST = [
'/',
'/main.html',
'/assets/style.css',
'/assets/app.js',
'/assets/icon.png'
];
// 安装阶段:打开缓存并预存资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_VERSION)
.then(cache => cache.addAll(PRECACHE_LIST))
.then(() => self.skipWaiting())
);
});
// 激活阶段:移除过期缓存版本
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(
keys.filter(key => key !== CACHE_VERSION)
.map(oldKey => caches.delete(oldKey))
)
).then(() => self.clients.claim())
);
});
// 捕获网络请求
self.addEventListener('fetch', event => {
const { request } = event;
// 对非 GET 请求或跨域资源直接放行
if (request.method !== 'GET' || new URL(request.url).origin !== location.origin) {
event.respondWith(fetch(request));
return;
}
event.respondWith(
caches.match(request).then(cachedRes => {
// 若缓存存在,立即返回
if (cachedRes) return cachedRes;
// 否则发起网络请求
return fetch(request).then(networkRes => {
// 验证响应有效性
if (!networkRes || networkRes.status !== 200 || networkRes.type === 'opaque') {
return networkRes;
}
// 克隆响应对象以同时用于返回和缓存
const clonedRes = networkRes.clone();
caches.open(CACHE_VERSION).then(cache => {
cache.put(request, clonedRes);
});
return networkRes;
});
})
);
});
2. 在主页面中注册 Service Worker
// main.js 或内联脚本
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(reg => console.log('SW 注册成功:', reg.scope))
.catch(err => console.error('SW 注册失败:', err));
});
}
3. 部署与测试
- 确保应用部署在 HTTPS 环境下(本地开发可使用
localhost)。 - 打开 Chrome DevTools → Application → Service Workers,启用 "Offline" 模式进行离线测试。
- 观察 Cache Storage 中是否生成对应缓存条目。
缓存策略选择
可根据不同资源类型采用灵活策略:
- 缓存优先:适用于静态资源,优先读取本地副本。
- 网络优先:适合动态内容,失败后降级到缓存。
- 后台更新:先返回缓存,再异步更新最新版本。
为何弃用 AppCache?
尽管 Application Cache 曾提供简单的离线功能,但其存在严重问题:
- 仅当 manifest 文件字节变化时才触发更新,导致缓存难以刷新。
- 缺乏细粒度控制,无法处理复杂场景。
- 容易造成"卡死"状态,用户无法获取更新后的资源。
- 调试信息不足,排查困难。
总结
Service Worker 是现代 Web 离线架构的基础组件,尤其在 PWA 开发中不可或缺。它赋予开发者对网络请求的完全控制权,配合 Cache API 可实现高效、可靠的离线体验。推荐所有需要离线支持的应用均采用此方案。