ServiceWorker

Service Worker

image-20250207174059456

一、概述

Service Worker 是一种在浏览器后台运行的脚本,与网页(主线程)完全分离。运行在独立线程中,不直接操作 DOM,而是通过事件机制与页面进行通信。它要求在 HTTPS 环境下运行(或 localhost 环境下开发)。

其主要作用包括:

拦截网络请求:通过监听 fetch 事件,Service Worker 能够捕获页面的网络请求,返回缓存内容或自定义响应。

离线支持:通过缓存静态资源,实现网络断线时依然可用的离线体验。

后台任务:支持后台同步(Background Sync)和推送通知(Push Notifications),即使页面关闭也可处理一些任务。

二、Service Worker 注册

在使用 Service Worker 之前,需要在网页的主线程中注册它。

通常的注册代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if ('serviceWorker' in navigator){
window.addEventListener('load',function(){
navigator.serviceWorker.register('../JS/16_sw.js')
.then(function(registration){
console.log('Service Worker 注册成功,作用域:',registration.scope);
})
.catch(function(error){
console.error('Service Worker 注册失败:',error);
})

})
}else{
console.log('当前浏览器不支持 Service Worker。');
}

说明:

  • 检查 serviceWorker 是否存在于 navigator 对象中,确保浏览器支持该特性。

  • 监听 load 事件确保页面资源加载完毕后再注册。

  • registration.scope 表示 Service Worker 控制的范围。

? navigator是什么

JavaScript 中的一个全局对象,提供了关于浏览器和操作系统的有用信息。它通常用于访问浏览器的状态、用户的设备信息以及其他与浏览器相关的功能。

1
navigator.userAgent  # 返回一个字符串,包含了浏览器的信息,如浏览器类型、版本、操作系统等
1
navigator.platform #  返回浏览器所在操作系统的名称。常见的返回值包括 "Win32", "Linux x86_64", "MacIntel" 等。
1
navigator.onLine # 返回一个布尔值,表示浏览器是否处于在线状态(即是否连接到互联网)
1
navigator.language # 返回浏览器的首选语言设置。通常返回类似 en-US、zh-CN 这样的语言标识符。
1
navigator.geolocation # 是一个对象,提供定位服务。
1
navigator.connection # 提供网络连接的详细信息,如网络类型(Wi-Fi、4G等)和带宽。
1
navigator.vendor # 返回浏览器的供应商名,如 "Google Inc.""Apple Computer, Inc." 等。
1
navigator.cookieEnabled / navigator.plugins / navigator.deviceMemory...

三、Service Worker 文件(sw.js)的代码示例

① 定义缓存名称和缓存资源列表

1
2
3
4
5
6
7
8
const CACHE_NAME = 'cache-v1';
const urlsToCache = [
'/',
// '/index.html',
// '/styles/main.css',
// '/scripts/main.js',
// '/images/logo.png'
]

② 安装事件 install

安装阶段,会预缓存一些必要的资源。

1
2
3
4
5
6
7
8
9
10
self.addEventListener('install',function(event){
console.log('service worker installing');
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache){
console.log('打开缓存,准备缓存资源');
return cache.addAll(urlsToCache) // 将资源添加到缓存中
})
)
})

**?**为什么要加上event.waitUntil

默认情况下,Service Worker 的事件处理函数是 同步执行 的。如果直接把异步操作(比如缓存文件)放在事件处理函数中,事件处理器会继续执行,并且很快结束,而异步操作可能还没有完成。so,为了确保事件处理函数等待异步操作完成后才结束,保证了操作的完整性。

caches是什么

Service Worker API 中的一个全局对象,caches 对象提供了几种常用的操作方法,比如:

1
caches.open(cacheName) # 打开一个缓存,如果缓存不存在,则会创建一个新的缓存。返回一个 Promise,该 Promise 成功时会返回一个 Cache 对象。
1
caches.match(request) # 查找缓存中是否已经有与给定请求(request)匹配的响应。如果找到,会返回一个 Response 对象。
1
caches.delete(cacheName) # 删除指定名称的缓存。返回一个 Promise,表示删除操作的结果。
1
caches.has(cacheName) # 检查指定的缓存是否存在。返回一个 Promise,如果存在,Promise 会返回 true,否则返回 false

③ 激活事件 activate

激活阶段通常用于清理旧版本缓存,确保客户端使用最新的资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
self.addEventListener('activate', function(event){
console.log('service worker activating');
event.waitUntil(
caches.keys().then(
function(cacheNames){
return Promise.all(
cacheNames.map(function(cacheName){
if(cacheName !== CACHE_NAME){
console.log('删除旧缓存:',cacheName);
return caches.delete(cacheName);
}
})
)
}
)
)

})

④ 拦截网络请求事件 fetch

通过监听 fetch 事件,Service Worker 可以拦截所有网络请求,并根据缓存情况返回相应资源,从而实现离线支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
self.addEventListener("fetch", function (event) {
console.log("拦截请求:", event.request.url);
event.respondWith( //让 Service Worker 对拦截的请求做出响应。
caches
.match(event.request) // 检查请求是否已在缓存中。
.then(function (response) {
if (response) {
return response;
}
return fetch(event.request).then(function (netResponse) {
// 可选:动态缓存新的资源
return caches.open(CACHE_NAME).then(function (cache) {
// 克隆响应后缓存,注意 response 只能使用一次
cache.put(event.request, netResponse.clone());
return netResponse;
});
});
})
.catch(function(err){
console.error(err)
})
);
});

? response 只能使用一次什么意思

在处理 HTTP 请求时,服务器的响应是一次性的。例如,收到一个 HTTP 响应后,你可以读取响应体、状态码等信息,但是如果你已经读取过响应体内容(比如通过 .json().text() 方法),这个响应体内容就不能再读取一次。第二次访问会抛出错误或返回 null,因为响应体已经被消耗了。这是因为大多数响应对象(比如 HTTP 响应)是 流式的,即数据是在网络传输过程中逐步到达的。读取数据时就会把它“消耗”掉,后续就无法再访问这些已经消耗的数据。为了避免重复读取,可以在获取数据时尽早存储一份副本。

⑤ 后台同步 sync

后台同步允许在网络恢复时执行延迟的任务。使用示例如下(需浏览器支持 Sync API):

1
2
3
4
5
6
7
8
9
10
11
12
self.addEventListener("sync", function (event) {
if (event.tag === "sync-tag") {
event.waitUntil(
// 这里放置需要在后台同步的任务,例如把离线保存的数据发送到服务器
sendDataToServer()
);
}
});
function sendDataToServer() {
// 模拟异步任务,如 fetch 请求
return fetch('/sync', { method: 'POST', body: JSON.stringify({ /* data */ }) });
}

event.tag 是什么

用来识别事件的类型或者标识某个特定的同步任务。后台同步允许在用户的网络连接恢复时,浏览器通过 Service Worker 自动发送请求来同步未完成的操作。例如,用户在离线时提交了一个表单,Service Worker 会将此请求保存并在恢复连接后进行同步。sync 事件会带有一个 tag,可以用它来区分不同的同步任务。

⑥ 推送通知 push

1
2
3
4
5
6
7
8
9
10
11
12
13
14
self.addEventListener('push',function(event){
console.log('接收到推送消息');
const data = event.data ? event.data.json() : {}
const title = data.title || '默认标题:安安第一棒!'
const options = {
body: data.body || '默认消息',
icon: '../Images/cat.jpg'
}

event.waitUntil(
self.registration.showNotification(title,options)
)

})

四、调试与注意事项

调试
现代浏览器(如 Chrome 和 Firefox)在开发者工具中提供了专门的 Service Worker 调试面板,可以查看注册情况、日志和缓存内容。

image-20250207221719935

HTTPS 环境
Service Worker 仅在 HTTPS 环境下工作(或在 localhost 开发环境中),以确保安全性。

注册:在主页面通过 navigator.serviceWorker.register() 注册 Service Worker 文件。

生命周期:包括 install(安装、预缓存资源)、activate(激活、清理旧缓存)以及 fetch(拦截网络请求)等事件。Service Worker 的安装和激活可能需要一些时间,更新机制(如 skipWaiting()clients.claim())可以帮助加速新版本的激活,但也需要谨慎使用。