Service Workers 是现代 Web 应用实现离线功能、后台同步和推送通知的核心技术。它是 PWA(Progressive Web App) 的核心组件,可彻底改变 Web 应用的性能和用户体验。
1. 核心概念与特性
特性 | 描述 |
---|---|
独立线程 | 运行在浏览器后台的独立 JavaScript 线程,不阻塞主线程。 |
网络代理 | 拦截和处理网络请求(如动态缓存资源)。 |
离线支持 | 缓存关键资源,使应用在无网络时仍可运行。 |
生命周期管理 | 通过安装(Install)、激活(Activate)等阶段控制更新逻辑。 |
持久化缓存 | 使用 Cache API 管理缓存,数据存储更可靠。 |
事件驱动 | 通过 fetch 、push 、sync 等事件响应操作。 |
2. 生命周期与关键阶段
- 注册(Registration)页面中注册 Service Worker,触发浏览器下载并解析脚本。
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('注册成功:', reg.scope))
.catch(err => console.error('注册失败:', err));
}
- 安装(Install)首次注册或检测到新版本时触发,通常用于缓存静态资源。
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache => {
return cache.addAll([
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png',
'/'
]);
})
);
});
- 激活(Activate)新 Service Worker 准备接管控制权时触发,用于清理旧缓存。
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.filter(name => name !== 'v1')
.map(name => caches.delete(name))
);
})
);
});
- 运行中(Running)接管页面后监听 fetch、push 等事件。
3. 核心 API 与使用
- 拦截请求(Fetch 事件)动态缓存策略示例(缓存优先,失败时回退到网络):
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 命中缓存则返回
if (response) return response;
// 否则请求网络并缓存
return fetch(event.request).then(res => {
return caches.open('v1').then(cache => {
cache.put(event.request, res.clone());
return res;
});
});
})
);
});
- 缓存策略模板策略实现方式适用场景缓存优先优先返回缓存,无缓存时请求网络并存储。静态资源(CSS/JS/图片)网络优先优先请求网络,失败时回退到缓存。实时数据(如API请求)仅缓存完全依赖缓存,无缓存则返回错误。完全离线场景仅网络仅从网络获取,不读写缓存。需要强制更新的数据
4. 作用域与更新机制
- 作用域(Scope)Service Worker 只能控制其所在目录及子目录的页面。示例:若 sw.js 位于 /scripts/,则作用域为 /scripts/*。可通过注册时指定 scope 参数调整:
navigator.serviceWorker.register('/sw.js', { scope: '/' });
- 版本更新浏览器通过字节对比检测 sw.js 是否更新。更新流程:新 Service Worker 进入 install 阶段(旧版本仍控制页面)。新版本安装完成后进入 waiting 状态。当所有旧版本控制的页面关闭后,新版本触发 activate。强制立即激活:
self.addEventListener('install', event => {
self.skipWaiting(); // 跳过等待阶段
});
5. 安全与兼容性
- HTTPS 要求Service Worker 仅在 HTTPS 或 localhost 环境下生效。
- 浏览器支持浏览器支持版本Chrome40+Firefox44+Safari11.1+Edge17+
- 调试工具Chrome DevTools → Application → Service Workers 面板可查看状态、强制更新和模拟离线。
6. 完整示例:离线优先应用
HTML(index.html)
<!DOCTYPE html>
<html>
<head>
<title>离线应用示例</title>
<link rel="stylesheet" href="/styles/main.css">
</head>
<body>
<h1>离线优先 Demo</h1>
<script src="/scripts/app.js"></script>
<script>
// 注册 Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
</body>
</html>
Service Worker(sw.js)
const CACHE_NAME = 'offline-v1';
const ASSETS = [
'/',
'/styles/main.css',
'/scripts/app.js',
'/images/fallback.png'
];
// 安装阶段:预缓存关键资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(ASSETS))
.then(() => self.skipWaiting())
);
});
// 激活阶段:清理旧缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys => {
return Promise.all(
keys.filter(key => key !== CACHE_NAME)
.map(key => caches.delete(key))
);
})
);
});
// 拦截请求:离线优先策略
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request)
.catch(() => caches.match('/images/fallback.png'));
})
);
});
7. 高级功能扩展
- 后台同步(Background Sync)允许在用户恢复网络连接后自动同步数据:
// 页面中请求同步
navigator.serviceWorker.ready.then(reg => {
reg.sync.register('sync-data');
});
// Service Worker 监听 sync 事件
self.addEventListener('sync', event => {
if (event.tag === 'sync-data') {
event.waitUntil(sendDataToServer());
}
});
- 推送通知(Push API)结合推送服务实现系统级通知:
self.addEventListener('push', event => {
const data = event.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icons/icon-192x192.png'
});
});
8. 最佳实践
- 按需缓存:避免一次性缓存过多资源,动态缓存用户高频访问内容。
- 版本控制:每次更新资源时修改缓存名称(如 CACHE_NAME = 'v2')。
- 错误处理:所有 Promise 链需包含 .catch() 避免静默失败。
- 性能优化:使用 Cache-Control 头管理 HTTP 缓存。对大型资源(如视频)使用流式响应。
9. 常见问题
- 为什么修改 sw.js 后不更新?浏览器默认每小时检查一次更新,可手动触发:
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.update();
});
- 如何强制清除所有缓存?开发者工具 → Application → Clear storage → 勾选 "Caches" 并清除。
通过掌握 Service Workers,开发者可为 Web 应用赋予原生应用般的离线能力和后台处理功能。结合 Workbox 等工具库可进一步简化复杂缓存策略的实现。