Service Worker的生命周期和全局对象和API
当我们注册了Service Worker后,它会经历生命周期的各个阶段,同时会触发相应的事件。整个生命周期包括了:installing --> installed --> activating --> activated --> redundant。当Service Worker安装(installed)完毕后,会触发install事件;而激活(activated)后,则会触发activate事件。
serviceWorker中,进程的全局对象(类似于window)叫做self,我们可以把生命周期监听挂载到这上面。
ExtendableEvent.waitUntil()
在说起self监听的各个事件之前,先介绍一下ExtendableEvent这个类,后续的很多返回事件都是继承了这个类的子类。
因此他们都可以使用ExtendableEvent.waitUntil()方法。
实际上,避免在sw中阻塞线程,sw提供的api基本都是异步,使用await / Promise去处理。
waitUntil接受一个Promise参数,可以保持长任务中线程的活性。
waitUntil方法解读
ExtendableEvent.waitUntil() 方法告诉事件分发器该事件仍在进行。这个方法也可以用于检测进行的任务是否成功。在服务工作线程中,这个方法告诉浏览器事件一直进行,直至 promise 解决,浏览器不应该在事件中的异步操作完成之前终止服务工作线程。
service worker 中的 install (en-US) 事件使用 waitUntil() 来将服务工作线程保持在 installing (en-US) 阶段。如果传入 waitUntil() 的 promise 被拒绝,则将此次安装视为失败,丢弃这个服务工作线程。这主要用于确保在服务工作线程安装以前,所有依赖的核心缓存都已经成功载入。
service worker 中的 activate (en-US) 事件使用 waitUntil() 来延迟函数事件,如 fetch 和 push,直至传入 waitUntil() 的 promise 被解决。这让服务工作线程有时间更新数据库架构(database schema)和删除过时缓存,让其他事件能在一个完成更新的状态下进行。
waitUntil() 方法最初必须在事件回调里调用,在此之后,方法可以被调用多次,直至所有传入的 promise 被解决。
register
service worker 的注册日志记录在 Chrome 浏览器中可以通过访问 chrome://serviceworker-internals
查看。
ServiceWorkerContainer.register()
如果成功,ServiceWorker会将scriptURL绑定到一个scope(页面路径上)。成功了会返回一个ServiceWorkerRegistration对象(下有介绍)。
ServiceWorkerContainer.register(scriptURL, options).then(
function (ServiceWorkerRegistration) {
// do something
},
).catch(e=>{
});
参数
- scriptURL service worker 脚本的 URL.
- options 可选 注册时提供选项的配置对象。目前可用的选项包括:
scope: 表示定义 service worker 注册范围的 URL;service worker 可以控制的 URL 范围。通常是相对 URL。默认值是基于当前的 location,并以此来解析传入的路径。
返回
返回一个 Promise 对象,值是 ServiceWorkerRegistration。
ServiceWorkerRegistration
在serviceWorker注册成功后,ServiceWorkerRegistration注册对象将挂载到sw线程全局对象上,使用self.registration就可以拿到注册对象了~
ServiceWorkerRegistration
它的原型是EventTarget对象,可以使用最基本的EventTarget.addEventListener()
、EventTarget.removeEventListener()
、EventTarget.dispatchEvent()
。
properties
-
active
初始值为null,返回一个处于激活中或已激活状态的服务工作线程。如果客户端的URL位于注册的作用域内(在首次调用ServiceWorkerContainer.register时设置的scope选项),则一个活跃的工作线程会控制一个Client。
-
installing
返回一个处于安装状态的服务工作线程。这一开始被设置为null。
安装完成之后,active会有值,installing的工作线程又回回到null。 -
pushManager
返回对PushManager接口的引用,用于管理推送订阅,包括订阅、获取活跃订阅以及访问推送权限状态。
一般用来注册webApp消息推送事件。
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: window.urlBase64ToUint8Array(publicKey)
}; // 生成对应的pushSubscription数据,用于标识用户与安全验证
registration.pushManager.subscribe(subscribeOptions).then(function (pushSubscription) {
console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
return pushSubscription;
});
methods
-
ServiceWorkerRegistration.getNotifications()
返回通过当前ServiceWorkerRegistration注册的一个通知列表。 -
ServiceWorkerRegistration.showNotification()
显示push的请求标题。
// 添加service worker对push的监听
self.addEventListener('push', function (e) {
var data = e.data;
if (e.data) {
data = data.json();
console.log('push的数据为:', data);
self.registration.showNotification(data.text);
}
else {
console.log('push没有任何数据');
}
});
- ServiceWorkerRegistration.unregister()
注销ServiceWorker并返回一个Promise。ServiceWorker将在注销之前完成所有正在进行的操作。
如果想要取消缓存、不想要用SW了可以用unregister. - ServiceWorkerRegistration.update()
在不查询缓存的情况下,检查ServiceWorker的更新版本。
install
InstallEvent
InstallEvent 接口表示一个 ServiceWorker 的 ServiceWorkerGlobalScope 上分派的安装操作。作为 ExtendableEvent 的一个子类,它确保在安装期间不调度诸如 FetchEvent 之类的功能事件。
一般来说我们在install这里缓存资源。具体代码放在下面。
值得一提的是instllEvent继承ExtendableEvent的一个方法waitUntil。上面已经介绍过了。
fetch
FetchEvent
这是会在 service worker 全局作用域中触发 fetch 事件的事件类型。它包含关于 fetch 的信息,包括 request 和接收方如何处理响应。它提供 event.respondWith() 方法,允许我们为此 fetch 提供一个响应。
self.addEventListener('fetch', function (fetchEvent) {
console.log(fetchEvent)
console.log('现在正在请求:' + fetchEvent.request.url);
});
我们监听fetch事件,返回的fetchEvent中是一个fetch请求的相关属性,介绍如下:
属性
-
clientId
发起 fetch 的同源客户端的 id -
request
浏览器想要发送的 Request。
如果我们想要看到这个请求的url,就可以使用request.url
方法
-
FetchEvent.respondWith()
阻止浏览器的默认 fetch 操作,并且由你自己提供一个响应(可以是一个 promise)。 -
ExtendableEvent.waitUntil()
和上面的installEvent一样,继承了ExtendableEvent类的对象都会有这个方法。
延长事件的生命周期。用于通知浏览器延长超出响应回复时间的任务,例如流和缓存。
activate
ServiceWorkerGlobalScope: activate event
MDN-删除缓存数据
PWA 应该在 service worker 的 activate (en-US) 事件中清除旧版本的任何缓存:当此事件触发时,service worker 可以确定没有之前版本的 service worker 正在运行,因此不再需要旧的缓存数据。
self.addEventListener("activate", (event) => {
const cacheAllowlist = ["v2"];
event.waitUntil(
caches.forEach((cache, cacheName) => {
if (!cacheAllowlist.includes(cacheName)) {
return caches.delete(cacheName);
}
}),
);
});
Web Workers API
MDN-渐进式 Web 应用(PWA)
Cache API
Cache
Cache 接口为缓存的 Request / Response 对象对提供存储机制,例如,作为ServiceWorker 生命周期的一部分。请注意,Cache 接口像 workers 一样,是暴露在 window 作用域下的。尽管它被定义在 service worker 的标准中,但是它不必一定要配合 service worker 使用。
一个域可以有多个命名 Cache 对象。你需要在你的脚本 (例如,在 ServiceWorker 中) 中处理缓存更新的方式。除非明确地更新缓存,否则缓存将不会被更新;除非删除,否则缓存数据不会过期。使用CacheStorage.open(cacheName)
打开一个 Cache 对象,再使用 Cache 对象的方法去处理缓存。
你需要定期地清理缓存条目,因为每个浏览器都硬性限制了一个域下缓存数据的大小。缓存配额使用估算值,可以使用StorageEstimate API
获得。浏览器尽其所能去管理磁盘空间,但它有可能删除一个域下的缓存数据。浏览器要么自动删除特定域的全部缓存,要么全部保留。确保按名称安装版本缓存,并仅从可以安全操作的脚本版本中使用缓存。
Cache.put, Cache.add和Cache.addAll只能在GET请求下使用。
Cache.add
Cache接口的 add() 方法接受一个 URL 作为参数,请求参数指定的 URL,并将返回的 response 对象添加到给定的 cache 中。
参数:
request 要添加到 cache 的 request。它可以是一个 Request 对象,也可以是 URL。
返回:
Promise
add() 方法在功能上等同于以下代码:
fetch(url).then(function (response) {
if (!response.ok) {
throw new TypeError("bad response status");
}
return cache.put(url, response);
});
使用示例:
下面的代码块等待 InstallEvent 事件触发,然后运行 waitUntil 来处理该应用程序的安装过程。包括调用 CacheStorage.open 来创建一个新的缓存,然后使用 Cache.add 来添加一个请求资源到该缓存。
this.addEventListener("install", function (event) {
event.waitUntil(
caches.open("v1").then(function (cache) {
return cache.add("/sw-test/index.html");
}),
);
});
Cache.addAll
Cache 接口的 addAll() 方法接受一个 URL 数组,检索它们,并将生成的 response 对象添加到给定的缓存中。在检索期间创建的 request 对象成为存储的 response 操作的 key。
使用示例:
此代码块等待一个 InstallEvent 事件触发,然后运行 waitUntil 来处理该应用程序的安装进程。包括调用 CacheStorage.open 创建一个新的 cache,然后使用 addAll() 添加一系列资源。
this.addEventListener("install", function (event) {
event.waitUntil(
caches.open("v1").then(function (cache) {
return cache.addAll([
"/sw-test/",
"/sw-test/index.html",
"/sw-test/style.css",
"/sw-test/app.js",
"/sw-test/image-list.js",
"/sw-test/star-wars-logo.jpg",
"/sw-test/gallery/",
"/sw-test/gallery/bountyHunters.jpg",
"/sw-test/gallery/myLittleVader.jpg",
"/sw-test/gallery/snowTroopers.jpg",
]);
}),
);
});
Cache.delete
Cache 接口的 delete() 方法查询 request 为 key 的 Cache 条目,如果找到,则删除该 Cache 条目并返回 resolve 为 true 的 Promise 。如果没有找到,则返回 resolve 为 false 的 Promise 。
cache.delete(request,{options}).then(function(true) {
//your cache entry has been deleted
});
参数 options:
- ignoreSearch: 一个 Boolean 值,指定匹配进程中是否忽略 url 中的查询字符串。如果设置为 true,http://foo.com/?value=bar 中的 ?value=bar 部分在执行匹配时会被忽略。默认为 false。
- cacheName: A DOMString that represents a specific cache to search within. Note that this option is ignored by Cache.delete().
Cache.keys
Cache 接口的 keys() 方法返回一个 Promise ,这个 Promise 将解析为一个Cache 键的数组。
请求将以它们被插入的顺序返回。
参数options同上
cache.keys(request, { options }).then(function (keys) {
//do something with your array of requests
});
Cache.match & Cache.matchAll
Cache 接口的 match() 方法,返回一个 Promise 解析为 (resolve to) 与 Cache 对象中的第一个匹配请求相关联的Response 。如果没有找到匹配,Promise 解析为 undefined。
matchAll匹配所有的请求。
参数:
request : 在Cache对象中查找的Request对象对应的 response。这个Request可以是 object 或者是一个 URL.
options:同上。
cache.match(request, { options }).then(function (response) {
//操作 response
});
Cache.put
Cache.add/Cache.addAll 不会缓存 Response.status 值不在 200 范围内的响应,而 Cache.put 允许你存储任何请求/响应对。因此,Cache.add/Cache.addAll 不能用于不透明的响应,而 Cache.put 可以。
与add用法相同,更推荐add。
CacheStorage API
CacheStorage在代码中就是通过caches (复数)
- delete()
- has()
- keys()
- match()
- open()
Clients API
Clients 提供对 Client 对象的访问。在 service worker 中使用 self.clients 使用。
Client
可以从 Clients.matchAll() 和 Clients.get() 等方法获取 Client/WindowClient 对象。
- Client.postMessage()
向客户端发送一条消息。 - Client.id
一个字符串,表示客户端的通用唯一标识。 - Client.type
表示客户端类型的字符串。可能是 “window”、“worker” 或 “sharedworker”。 - Client.url
表示客户端 URL 的字符串。
Clients方法
-
Clients.get()
返回一个匹配给定 id 的 Client 的 Promise . -
Clients.matchAll()
返回一个 Client 对象数组的 Promise . options 参数允许你控制返回的 clients 类型。 -
Clients.openWindow()
打开给定 URL 的新浏览器窗口,并返回新 WindowClient a 的 Promise . -
Clients.claim()
允许一个激活的 service worker 将自己设置为其scope 内所有 clients 的 controller .