Service Worker 浏览器兼容性详解
Service Worker 作为现代 Web 技术,在不同浏览器中的支持程度不同。了解兼容性情况并提供降级方案是开发 PWA 的重要环节。
浏览器支持情况
主流浏览器支持度
| 浏览器 | 版本 | 支持情况 |
|---|
| Chrome | 45+ | ✅ 完全支持 |
| Firefox | 44+ | ✅ 完全支持 |
| Safari | 11.1+ | ✅ 支持(部分功能受限) |
| Edge | 17+ | ✅ 完全支持 |
| IE | 所有版本 | ❌ 不支持 |
| Opera | 32+ | ✅ 完全支持 |
| iOS Safari | 11.3+ | ✅ 支持(部分功能受限) |
| Android Chrome | 45+ | ✅ 完全支持 |
| Samsung Internet | 4+ | ✅ 完全支持 |
功能兼容性详细对比
| 功能 | Chrome | Firefox | Safari | Edge |
|---|
| Service Worker 基础 | ✅ | ✅ | ✅ | ✅ |
| Cache API | ✅ | ✅ | ✅ | ✅ |
| Push API | ✅ | ✅ | ✅ (16.4+) | ✅ |
| Background Sync | ✅ | ❌ | ❌ | ✅ |
| Periodic Background Sync | ✅ | ❌ | ❌ | ✅ |
| Notification | ✅ | ✅ | ✅ | ✅ |
| Add to Home Screen | ✅ | ✅ | ✅ (部分) | ✅ |
兼容性检测
基础功能检测
// 检测 Service Worker 支持
function isServiceWorkerSupported() {
return 'serviceWorker' in navigator;
}
// 检测 Cache API 支持
function isCacheAPISupported() {
return 'caches' in window;
}
// 检测 Push API 支持
function isPushAPISupported() {
return 'PushManager' in window;
}
// 检测 Background Sync 支持
function isBackgroundSyncSupported() {
return 'sync' in ServiceWorkerRegistration.prototype;
}
// 检测 Notification 支持
function isNotificationSupported() {
return 'Notification' in window;
}
综合兼容性检测
// 详细的兼容性检测
function checkServiceWorkerCompatibility() {
const features = {
serviceWorker: 'serviceWorker' in navigator,
cacheAPI: 'caches' in window,
pushAPI: 'PushManager' in window,
backgroundSync: 'sync' in ServiceWorkerRegistration.prototype,
periodicSync: 'periodicSync' in ServiceWorkerRegistration.prototype,
notification: 'Notification' in window,
addToHomeScreen: 'BeforeInstallPromptEvent' in window,
backgroundFetch: 'BackgroundFetchManager' in window
};
const supportedFeatures = Object.entries(features)
.filter(([_, supported]) => supported)
.map(([name]) => name);
const unsupportedFeatures = Object.entries(features)
.filter(([_, supported]) => !supported)
.map(([name]) => name);
console.log('支持的特性:', supportedFeatures);
console.log('不支持的特性:', unsupportedFeatures);
return {
isFullySupported: features.serviceWorker && features.cacheAPI,
features,
supportedFeatures,
unsupportedFeatures
};
}
// 使用示例
const compatibility = checkServiceWorkerCompatibility();
if (!compatibility.isFullySupported) {
console.warn('当前浏览器不完全支持 Service Worker');
}
渐进增强策略
1. 基础降级方案
// 主线程代码
if ('serviceWorker' in navigator) {
// 支持 Service Worker,正常注册
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker 注册成功:', registration);
})
.catch(error => {
console.error('Service Worker 注册失败:', error);
// 启用降级方案
enableFallbackMode();
});
} else {
// 不支持 Service Worker,启用降级方案
console.log('浏览器不支持 Service Worker');
enableFallbackMode();
}
// 降级模式
function enableFallbackMode() {
// 1. 使用传统的 localStorage/sessionStorage 缓存
// 2. 禁用离线功能
// 3. 显示提示信息
document.body.classList.add('no-sw-support');
// 显示提示
const banner = document.createElement('div');
banner.className = 'compatibility-banner';
banner.innerHTML = `
<p>您的浏览器不支持离线功能,请使用现代浏览器获得最佳体验</p>
<button onclick="this.parentElement.remove()">知道了</button>
`;
document.body.appendChild(banner);
}
2. 功能分级支持
// 根据支持的功能级别提供不同体验
class PWACompatManager {
constructor() {
this.level = this.detectSupportLevel();
this.init();
}
detectSupportLevel() {
if (!('serviceWorker' in navigator)) {
return 'basic'; // 基础模式
}
if (!('sync' in ServiceWorkerRegistration.prototype)) {
return 'standard'; // 标准模式(无后台同步)
}
if (!('periodicSync' in ServiceWorkerRegistration.prototype)) {
return 'advanced'; // 高级模式(无定期同步)
}
return 'full'; // 完整模式
}
init() {
switch (this.level) {
case 'full':
this.enableAllFeatures();
break;
case 'advanced':
this.enableAdvancedFeatures();
break;
case 'standard':
this.enableStandardFeatures();
break;
case 'basic':
this.enableBasicFeatures();
break;
}
}
enableAllFeatures() {
console.log('启用所有功能');
this.registerServiceWorker();
this.enablePushNotifications();
this.enableBackgroundSync();
this.enablePeriodicSync();
}
enableAdvancedFeatures() {
console.log('启用高级功能(无定期同步)');
this.registerServiceWorker();
this.enablePushNotifications();
this.enableBackgroundSync();
}
enableStandardFeatures() {
console.log('启用标准功能(无后台同步)');
this.registerServiceWorker();
this.enablePushNotifications();
// 使用 setTimeout 模拟后台同步
this.simulateBackgroundSync();
}
enableBasicFeatures() {
console.log('启用基础功能(仅在线模式)');
// 使用 localStorage 缓存
this.enableLocalStorageCache();
// 显示升级提示
this.showUpgradePrompt();
}
registerServiceWorker() {
navigator.serviceWorker.register('/sw.js');
}
enablePushNotifications() {
if ('Notification' in window) {
Notification.requestPermission();
}
}
enableBackgroundSync() {
// 实现后台同步
}
enablePeriodicSync() {
// 实现定期同步
}
simulateBackgroundSync() {
// 使用 setInterval 模拟
setInterval(() => {
if (navigator.onLine) {
this.syncPendingData();
}
}, 60000);
}
enableLocalStorageCache() {
// 使用 localStorage 实现简单缓存
}
showUpgradePrompt() {
// 显示浏览器升级提示
}
}
// 初始化
const pwaManager = new PWACompatManager();
3. Polyfill 方案
// Cache API Polyfill(简化版)
if (!('caches' in window)) {
window.caches = {
_cacheStorage: new Map(),
open(cacheName) {
if (!this._cacheStorage.has(cacheName)) {
this._cacheStorage.set(cacheName, new Map());
}
const cache = this._cacheStorage.get(cacheName);
return Promise.resolve({
match(request) {
const url = typeof request === 'string' ? request : request.url;
const item = cache.get(url);
if (item && Date.now() - item.timestamp < 3600000) {
return Promise.resolve(new Response(item.body));
}
return Promise.resolve(undefined);
},
put(request, response) {
const url = typeof request === 'string' ? request : request.url;
return response.text().then(body => {
cache.set(url, { body, timestamp: Date.now() });
});
},
delete(request) {
const url = typeof request === 'string' ? request : request.url;
return Promise.resolve(cache.delete(url));
},
keys() {
return Promise.resolve(Array.from(cache.keys()).map(url => new Request(url)));
}
});
},
keys() {
return Promise.resolve(Array.from(this._cacheStorage.keys()));
},
delete(cacheName) {
return Promise.resolve(this._cacheStorage.delete(cacheName));
},
match(request) {
const promises = Array.from(this._cacheStorage.values()).map(cache => {
const url = typeof request === 'string' ? request : request.url;
const item = cache.get(url);
return item ? new Response(item.body) : undefined;
});
return Promise.all(promises).then(results => {
return results.find(response => response !== undefined);
});
}
};
}
特定浏览器处理
Safari 特殊处理
// Safari 有一些特殊限制
function handleSafariSpecifics() {
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isSafari) {
// Safari 需要用户交互才能显示通知
document.addEventListener('click', () => {
if (Notification.permission === 'default') {
Notification.requestPermission();
}
}, { once: true });
// Safari 的 Service Worker 有一些限制
// 例如:某些情况下不会自动更新
setInterval(() => {
navigator.serviceWorker.ready.then(registration => {
registration.update();
});
}, 60 * 60 * 1000); // 每小时检查更新
}
}
iOS 特殊处理
// iOS 有一些特殊限制
function handleIOSSpecifics() {
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
if (isIOS) {
// iOS 的存储限制更严格
// 需要更积极地清理缓存
// iOS 的 Service Worker 在后台运行时间有限
// 需要优化同步策略
// iOS 的 Add to Home Screen 需要特殊处理
if ('standalone' in navigator) {
// 已经在主屏幕模式运行
console.log('Running in standalone mode');
}
}
}
测试策略
1. 浏览器测试矩阵
// 测试不同浏览器和版本
const testMatrix = [
{ browser: 'Chrome', version: 'latest', os: 'Windows' },
{ browser: 'Chrome', version: 'latest', os: 'macOS' },
{ browser: 'Chrome', version: 'latest', os: 'Android' },
{ browser: 'Firefox', version: 'latest', os: 'Windows' },
{ browser: 'Safari', version: 'latest', os: 'macOS' },
{ browser: 'Safari', version: 'latest', os: 'iOS' },
{ browser: 'Edge', version: 'latest', os: 'Windows' }
];
2. 功能检测测试
// 自动化兼容性测试
async function runCompatibilityTests() {
const tests = {
'Service Worker 注册': async () => {
if (!('serviceWorker' in navigator)) return 'skipped';
const reg = await navigator.serviceWorker.register('/sw.js');
return reg ? 'passed' : 'failed';
},
'Cache API 使用': async () => {
if (!('caches' in window)) return 'skipped';
const cache = await caches.open('test');
await cache.put('/test', new Response('test'));
const response = await cache.match('/test');
return response ? 'passed' : 'failed';
},
'推送通知': async () => {
if (!('Notification' in window)) return 'skipped';
const permission = await Notification.requestPermission();
return permission === 'granted' ? 'passed' : 'denied';
}
};
const results = {};
for (const [name, test] of Object.entries(tests)) {
try {
results[name] = await test();
} catch (error) {
results[name] = `error: ${error.message}`;
}
}
console.table(results);
return results;
}
最佳实践
- 渐进增强:基础功能在所有浏览器工作,高级功能渐进启用
- 功能检测:使用特性检测而非浏览器检测
- 优雅降级:为不支持的功能提供替代方案
- 用户提示:告知用户浏览器支持情况
- 持续测试:定期测试不同浏览器兼容性
- 监控上报:收集用户浏览器的兼容性数据