目录
HTML5 Service Workers是一种在浏览器后台运行的脚本,它独立于网页主线程,允许开发者拦截和控制网络请求,提供离线缓存、推送通知等功能,从而显著提升Web应用的用户体验和可靠性。
Service Workers基础
注册与生命周期:
- 注册:在网页中使用navigator.serviceWorker.register()方法注册Service Worker脚本。通常在主页面的
- 生命周期:Service Worker经历安装(install)、激活(activate)、闲置(idle)、接收到事件(event received)和终止(terminate)等状态。开发者通过Service Worker脚本中的事件监听器(如install、activate、fetch等)来处理各个阶段的任务。
创建Service Worker脚本
- Service Worker脚本位置:通常放置在网站的根目录或子目录下,如/sw.js。确保脚本可通过HTTPS协议访问,因为Service Workers仅在安全上下文中工作。
- 缓存策略:在Service Worker脚本中定义缓存策略,决定哪些资源应被缓存以及何时更新缓存。
// sw.js
// 定义缓存名称
const CACHE_NAME = 'my-offline-app-v1';
// 要缓存的资源列表
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/images/icon.png',
// 其他资源...
];
// 安装阶段
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(urlsToCache);
})
);
});
// 激活阶段
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
})
);
});
缓存与网络请求处理
- fetch事件:Service Worker监听fetch事件,当浏览器发起网络请求时,可以拦截并返回缓存中的资源,或者在网络请求失败时提供备用资源。
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
// 如果缓存中有请求的资源,则直接返回
if (cachedResponse) {
return cachedResponse;
}
// 否则,尝试从网络获取
return fetch(event.request).then((networkResponse) => {
// 将网络响应副本存入缓存(可选,取决于策略)
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, networkResponse.clone());
});
return networkResponse;
}).catch(() => {
// 网络请求失败,返回离线页面(或自定义错误页面)
return caches.match('/offline.html');
});
})
);
});
更新缓存
-
更新策略:当Service Worker脚本发生变动时,浏览器会自动尝试更新。在
install事件中,可以使用新的缓存名称来创建新的缓存,同时在activate事件中清理旧的缓存。 -
主动触发更新:在网页中可以通过定期检查Service Worker脚本的更新(如使用
navigator.serviceWorker.controller?.registration.update()),或者在Service Worker中监听message事件,由网页发送更新指令来主动触发更新。
其他功能
推送通知:通过Service Worker的push和notificationclick事件,可以实现Web Push通知功能,即使用户不在应用内也能接收到通知。
后台同步:利用sync事件,可在网络恢复时执行延迟的后台任务,如离线时保存的数据同步到服务器。
测试与调试
开发者工具:使用浏览器的开发者工具(如Chrome DevTools的"Application"面板),可以查看已注册的Service Workers、缓存内容、推送订阅状态等,以及模拟离线状态进行测试。
日志输出:在Service Worker脚本中使用console.log()记录日志,帮助调试。
Service Works应用示例
sw.js(Service Worker脚本)
// 定义缓存名称
const CACHE_NAME = 'my-offline-app-v1';
// 要缓存的资源列表
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/images/icon.png',
// 其他资源...
];
// 安装阶段
self.addEventListener('install', (event) => {
console.log('[Service Worker] Installing...');
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('[Service Worker] Caching app shell');
return cache.addAll(urlsToCache);
})
);
});
// 激活阶段
self.addEventListener('activate', (event) => {
console.log('[Service Worker] Activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
console.log(`[Service Worker] Removing old cache ${cacheName}`);
return caches.delete(cacheName);
}
})
);
})
);
console.log('[Service Worker] Activated');
});
// 缓存与网络请求处理
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
console.log(`[Service Worker] Serving from cache: ${event.request.url}`);
return cachedResponse;
}
console.log(`[Service Worker] Fetching: ${event.request.url}`);
return fetch(event.request).then((networkResponse) => {
// 可选:缓存网络响应
// caches.open(CACHE_NAME).then((cache) => {
// cache.put(event.request, networkResponse.clone());
// });
return networkResponse;
}, () => {
// 网络请求失败,返回离线页面(或自定义错误页面)
return caches.match('/offline.html');
});
})
);
});
index.html(主页面)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Worker Demo</title>
<link rel="stylesheet" href="/styles.css">
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('[App] Service Worker registered:', registration);
})
.catch((error) => {
console.error('[App] Service Worker registration failed:', error);
});
});
} else {
console.log('[App] Service Worker is not supported');
}
</script>
</head>
<body>
<h1>Welcome to the Service Worker Demo</h1>
<p>This page and its resources are cached for offline use.</p>
<img src="/images/icon.png" alt="Icon">
<script src="/app.js"></script>
</body>
</html>
- 在sw.js中,定义了缓存名称(CACHE_NAME)和要缓存的资源列表(urlsToCache)。在install事件处理器中,打开缓存并添加指定资源。在activate事件处理器中,删除旧的缓存。
- 在fetch事件处理器中,优先返回缓存中的资源,否则尝试网络请求。网络请求失败时,返回离线页面(这里假设存在/offline.html)。
- 在index.html中,检查浏览器是否支持Service Worker。支持的话,在页面加载完成后注册sw.js。在控制台可以看到Service Worker的注册状态和日志输出。
Web Worker的基本使用
Web Worker是HTML5提供的一个API,允许在浏览器环境中创建后台线程,从而实现Web应用程序的多线程处理。由于JavaScript在浏览器端默认是单线程执行的,对于长时间运行的计算、大数据处理、密集型计算等任务,如果不进行适当的分离,可能会导致用户界面卡顿或无响应。Web Worker通过创建独立的工作线程,可以在不影响主线程(即UI线程)的情况下执行这些任务,提升用户体验。
-
创建Worker: 使用new Worker(url)创建一个Worker实例,其中url是包含Worker脚本的URL。
-
通信:
- 主线程向Worker发送消息:使用worker.postMessage(data)方法,data可以是任意序列化后的JavaScript值。
- Worker向主线程发送消息:在Worker脚本中使用self.postMessage(data)。
- 接收消息:
- 主线程:通过监听worker.onmessage事件,处理来自Worker的消息。
- Worker:在Worker脚本中使用self.addEventListener('message', handler)来添加消息处理器。
- 生命周期管理:
- 启动:创建Worker实例即启动线程。
- 终止:调用worker.terminate()方法停止Worker线程。
Web Worker应用示例
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Worker Example</title>
</head>
<body>
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
<div id="result">Result: Loading...</div>
<script>
let myWorker;
function startWorker() {
if (!window.Worker) {
alert("Your browser does not support Web Workers.");
return;
}
myWorker = new Worker('worker.js');
myWorker.onmessage = function(e) {
document.getElementById('result').textContent = 'Result: ' + e.data;
};
myWorker.postMessage([1, 2, 3, 4, 5, ...Array(1000000).fill(0).map((_, i) => i + 6)]);
}
function stopWorker() {
if (myWorker) {
myWorker.terminate();
myWorker = null;
document.getElementById('result').textContent = 'Result: Worker stopped';
}
}
</script>
</body>
</html>
Worker脚本(worker.js):
// This code runs in a separate thread
self.addEventListener('message', function(e) {
const numbers = e.data;
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
self.postMessage(sum);
}, false);
在这个示例中:
- 当用户点击“Start Worker”按钮时,创建一个新的Web Worker实例,指向worker.js脚本。
- 向Worker发送一个包含大量数字的大数组。
- Worker接收到消息后,计算数组的累加和。
- 计算完成后,Worker通过postMessage将结果发送回主线程。
- 主线程接收到消息后,更新页面显示结果。
- 用户点击“Stop Worker”按钮时,终止Worker线程。