7.4 Service Worker 简介
7.4 Service Worker 简介
Service Worker 是现代Web平台的核心技术之一,它充当了Web应用程序与网络之间的代理层,赋予开发者对缓存策略和离线体验的精细控制能力。作为PWA(渐进式Web应用)的基石,Service Worker彻底改变了Web应用的离线能力和性能表现。
核心特性与工作原理
1. 基本特征
- 独立线程运行:与主JavaScript线程分离,不阻塞UI
- 可编程网络代理:拦截和处理网络请求
- 完全异步设计:基于Promise实现
- 生命周期独立:与关联页面无关,可自主运行
- HTTPS要求:生产环境必须使用安全连接(localhost除外)
2. 生命周期阶段
stateDiagram-v2
[*] --> 解析(Installing): 首次注册
解析(Installing) --> 安装完成(Installed): install事件完成
安装完成(Installed) --> 激活中(Activating): 等待旧SW失效
激活中(Activating) --> 激活完成(Activated): activate事件完成
激活完成(Activated) --> 闲置(Idle): 无任务
闲置(Idle) --> 终止(Terminated): 节省内存
终止(Terminated) --> 闲置(Idle): 新消息到达
激活完成(Activated) --> [*]: 被新版本替换
基础实现步骤
1. 注册Service Worker
// 主线程代码(通常是main.js)
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('注册成功,作用域:', registration.scope);
// 检测更新
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
console.log('发现新版本:', newWorker.state);
});
})
.catch(err => {
console.error('注册失败:', err);
});
});
}
2. 基本Service Worker文件(sw.js)
// 定义缓存名称和预缓存资源
const CACHE_NAME = 'my-site-cache-v1';
const PRECACHE_URLS = [
'/',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png'
];
// 安装阶段 - 预缓存关键资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('已打开缓存');
return cache.addAll(PRECACHE_URLS);
})
.then(() => self.skipWaiting()) // 跳过等待直接激活
);
});
// 激活阶段 - 清理旧缓存
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (!cacheWhitelist.includes(cacheName)) {
console.log('删除旧缓存:', cacheName);
return caches.delete(cacheName);
}
})
);
})
.then(() => self.clients.claim()) // 立即控制所有客户端
);
});
// 请求拦截 - 基本缓存优先策略
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
高级缓存策略
1. 动态缓存策略组合
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// API请求使用网络优先
if (url.pathname.startsWith('/api/')) {
event.respondWith(
fetch(event.request)
.then(response => {
// 成功获取则缓存响应
const clone = response.clone();
caches.open('api-cache-v1').then(cache => {
cache.put(event.request, clone);
});
return response;
})
.catch(() => {
// 网络失败返回缓存
return caches.match(event.request);
})
);
}
// 静态资源使用缓存优先
else if (PRECACHE_URLS.includes(url.pathname)) {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
}
// 其他请求尝试网络
else {
event.respondWith(fetch(event.request));
}
});
2. 缓存过期与更新
// 定期清理过期缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
// 检查缓存版本
if (!cacheName.startsWith('my-app-')) return;
return caches.open(cacheName).then(cache => {
return cache.keys().then(requests => {
return Promise.all(
requests.map(request => {
// 超过7天的缓存删除
return cache.match(request).then(response => {
if (!response) return;
const cachedDate = new Date(
response.headers.get('date')
);
const ageInDays = (Date.now() - cachedDate) / (1000 * 60 * 60 * 24);
if (ageInDays > 7) {
return cache.delete(request);
}
});
})
);
});
});
})
);
})
);
});
消息通信机制
1. 主线程与Service Worker通信
主线程发送消息:
// 页面发送消息
navigator.serviceWorker.controller.postMessage({
type: 'SYNC_DATA',
payload: { /* 数据 */ }
});
// 接收Service Worker消息
navigator.serviceWorker.addEventListener('message', event => {
if (event.data.type === 'UPDATE_AVAILABLE') {
showUpdateNotification();
}
});
Service Worker处理消息:
self.addEventListener('message', event => {
if (event.data.type === 'SYNC_DATA') {
// 处理数据同步
event.waitUntil(
handleSyncData(event.data.payload)
.then(() => {
// 回复消息
event.ports[0].postMessage({ status: 'SYNC_COMPLETE' });
})
);
}
});
// 主动向客户端发送消息
self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage({
type: 'CACHE_UPDATED',
payload: { version: '1.2.0' }
});
});
});
2. 后台同步(Background Sync)
// 主线程注册同步任务
navigator.serviceWorker.ready.then(registration => {
registration.sync.register('sync-comments')
.then(() => console.log('后台同步已注册'))
.catch(err => console.error('同步注册失败', err));
});
// Service Worker处理同步事件
self.addEventListener('sync', event => {
if (event.tag === 'sync-comments') {
event.waitUntil(
sendPendingComments()
.then(() => console.log('评论同步成功'))
.catch(() => console.log('评论同步失败'))
);
}
});
调试与最佳实践
1. Chrome开发者工具指南
-
Application面板:
- 查看注册的Service Worker
- 手动触发更新/卸载
- 模拟离线状态
-
离线测试技巧:
// 强制进入离线状态 self.addEventListener('fetch', event => { if (event.request.url.includes('test-offline')) { return new Response('离线模拟响应', { headers: { 'Content-Type': 'text/html' } }); } }); -
日志记录建议:
const DEBUG = true; function log(...args) { if (DEBUG) { console.log('[SW]', ...args); } } self.addEventListener('install', event => { log('安装事件触发'); // ... });
2. 性能优化策略
-
缓存策略选择:
策略 适用场景 实现方式 缓存优先 静态资源 `caches.match() 网络优先 频繁更新的内容 fetch().catch(caches.match)仅缓存 关键离线资源 caches.match()仅网络 实时数据 fetch() -
资源版本控制:
const ASSET_VERSION = 'v1.2'; const CACHE_NAME = `app-cache-${ASSET_VERSION}`; function getCacheKey(url) { const parsed = new URL(url); return `${parsed.pathname}?v=${ASSET_VERSION}`; } -
预加载关键资源:
self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => { const criticalResources = [ '/', '/app-shell', '/styles/main.css' ]; return cache.addAll(criticalResources); }) ); });
实际应用案例
1. 离线新闻阅读应用
// sw.js
const CACHE_NAME = 'news-app-v2';
const DYNAMIC_CACHE = 'news-dynamic-v1';
const API_CACHE = 'news-api-v1';
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll([
'/',
'/index.html',
'/styles/app.css',
'/scripts/app.js',
'/images/offline.png'
]))
);
});
self.addEventListener('fetch', event => {
const { request } = event;
const url = new URL(request.url);
// API请求缓存策略
if (url.pathname.startsWith('/api/news')) {
event.respondWith(
caches.open(API_CACHE).then(cache => {
return fetch(request).then(networkResponse => {
cache.put(request, networkResponse.clone());
return networkResponse;
}).catch(() => {
return cache.match(request)
.then(response => response || caches.match('/offline.json'));
});
})
);
}
// 静态资源策略
else {
event.respondWith(
caches.match(request).then(response => {
return response || fetch(request).then(networkResponse => {
return caches.open(DYNAMIC_CACHE).then(cache => {
if (request.method === 'GET') {
cache.put(request, networkResponse.clone());
}
return networkResponse;
});
}).catch(() => {
if (request.headers.get('accept').includes('text/html')) {
return caches.match('/offline.html');
}
});
})
);
}
});
2. 自动更新提示
// 主线程检测更新
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.addEventListener('updatefound', () => {
const newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
showUpdateToast('新版本可用,点击刷新');
}
});
});
});
// 用户点击刷新
document.getElementById('refresh-btn').addEventListener('click', () => {
navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING' });
});
// Service Worker接收跳过等待命令
self.addEventListener('message', event => {
if (event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
self.addEventListener('controllerchange', () => {
window.location.reload();
});
安全注意事项
-
HTTPS强制要求:
- 生产环境必须部署HTTPS
- 本地开发可使用localhost
-
内容安全策略(CSP):
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' api.example.com; -
敏感数据保护:
- 不要缓存隐私相关资源
- 及时清理认证令牌
-
跨域资源限制:
// 缓存跨域资源需设置CORS fetch('https://cdn.example.com/image.jpg', { mode: 'cors' }).then(response => { if (response.ok) { cache.put(request, response); } });
Service Worker作为现代Web能力的关键组件,通过合理运用可以实现:
- 显著的性能提升(即时加载)
- 完整的离线体验
- 后台数据同步
- 推送通知等高级功能
建议结合Workbox等工具库简化开发流程,同时遵循渐进增强原则,确保在不支持的浏览器中应用仍可正常运行。
#前端开发
分享于 2025-05-20