这里有几件事要介绍:
单一范围内的多个 Service Worker
对于给定的范围,只能有一个活动的 Service Worker。如果您尝试注册两个具有相同范围的不同服务工作者脚本,则第二次注册将触发service worker update flow:
// There's an implied default scope of '/'.
// See https://stackoverflow.com/a/33881341/385997
navigator.serviceWorker.register('/sw1.js');
// If called later on, this will trigger the update flow.
// You'll only end up with one of the two being active.
navigator.serviceWorker.register('/sw2.js');
sw2.js 何时激活并控制任何现有客户端的确切时间取决于您是否在sw2.js 中使用self.skipWaiting() 和self.clients.claim()。一旦sw2.js 激活,sw1.js 将被标记为冗余。
问我认为同一个问题的另一种方式是,您是否可以让多个服务人员同时控制同一个客户端页面。答案是否定的,您最多可以让一个 Service Worker 控制任何客户端页面,并且只有该 Service Worker 能够响应来自该页面的 fetch 事件。
使用 importScripts 共享通用处理程序
与其尝试在同一范围内注册多个服务工作者,不如使用importScripts() 来提取在不同 JavaScript 文件中定义的逻辑听起来是一种合理的方法。以这种方式使用 importScripts() 时需要注意以下几点:
-
importScripts() 需要在服务工作者代码的初始启动执行期间调用,而不是在事件处理程序中。 IE。不支持 importScripts() 的“延迟加载”。
-
importScripts() 按照列出的顺序,一个一个同步地执行文件中的所有代码。您可以在自己导入的文件中包含多个 importScripts() 或 importScripts(),它们都将按定义的顺序执行。
- 在导入的脚本中,
self 将设置为与代码位于顶级服务工作者中时使用的相同的ServiceWorkerGlobalScope。即,在导入的脚本内部或顶级 Service Worker 内部调用 self.addEventListener() 没有区别。
- (这与您的问题没有直接关系,但很高兴知道:)默认情况下,将缓存通过
importScripts() 引用的文件,使用浏览器内置的相同机制缓存您的顶级服务工作者文件。虽然服务工作者规范正在进行一些更改以改变这一点,但截至目前,只要文件名不改变,那些缓存的 importScripts() 文件将无限期使用。因此,最佳做法是在 importScripts() 引用的任何文件名中包含版本号或哈希。
多个获取事件处理程序
当您多次调用self.addEventListener('fetch') 时会发生什么?
从上一节中我们知道,这些多个调用是源自 importScripts() 资源内部还是顶级服务工作者内部无关紧要。它们都在同一个全局范围内运行。
行为定义明确:当客户端页面发出请求时,它将按照注册顺序逐一触发控制 Service Worker 的 fetch 处理程序 ,直到第一次调用event.respondWith()。一个fetch 事件处理程序调用respondWith(),不会触发其他获取事件处理程序,并且该处理程序的唯一责任是(最终)将Response 返回到客户端页面。
由于self.addEventlistener('fetch') 调用的顺序很重要,请确保以适当的顺序列出importScripts() 中的文件,并在定义任何fetch 之前或之后包括对importScripts() 的调用顶级 Service Worker 中的事件处理程序,具体取决于您希望优先处理的事件。
虽然您可以使用条件逻辑来确定是否调用event.respondWith(),但该逻辑不能是异步的,因为服务工作者不会等待查看是否调用了event.respondWith()。它需要同步移动到下一个event 处理程序(假设有一个)。
所以在 fetch 处理程序中,您可以使用类似的条件逻辑
// This can be executed synchronously.
if (event.request.url.endsWith('.html')) {
event.respondWith(...);
}
但您不能使用以下条件逻辑:
// caches.match() is asynchronous, and the service worker will have
// moved on to the next `fetch` handler before it completes.
caches.match('index.html').then(response => {
if (response) {
event.respondWith(...);
}
});
如果您想亲自查看多处理程序的行为,可以探索live code sample。