共享跨多个服务工作者定义的获取处理程序逻辑

Sch*_*cat 1 javascript offline-caching service-worker

此讨论之后,哪里有评论可言

修补Fetch

通过重写self.fetch,self.XMLHttpRequest和self.caches(对于cache.add/addAll)?看起来这些会让你拦截网络请求并在主SW脚本看到它们之前操纵响应.

我一直在寻找任何有关这些东西的文档,似乎找不到任何文档.

在我只需要 multiple服务工作者共存于一个范围的情况下,

在其中一个importScripts中导入另一个的事件处理程序后,

我是如何修补fetch /避免获取竞争/让两个fetch处理程序都工作?

Jef*_*ick 12

这里有几件事要介绍:

单一范围内的多个服务人员

给定范围只能有一个活动服务工作者.如果您尝试注册两个具有相同范围的不同服务工作者脚本,则第二个注册将触发服务工作者更新流程:

// 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');
Run Code Online (Sandbox Code Playgroud)

何时sw2.js激活并控制任何现有客户端的确切时间取决于您是否正在使用self.skipWaiting()self.clients.claim()内部sw2.js.一旦sw2.js激活,sw1.js将被标记为冗余.

另一种询问我认为是同一个问题的方法是,您是否可以让多个服务工作者同时控制同一个客户端页面.答案是否定的,您最多可以拥有一个控制任何客户端页面的服务工作者,并且只有该服务工作人员才能响应fetch源自该页面的事件.

使用importScripts共享公共处理程序

而不是尝试注册具有相同范围的多个服务工作者,使用importScripts()拉入在不同JavaScript文件中定义的逻辑听起来像一个合理的方法.importScripts()以这种方式使用时,请记住以下几点:

  • importScripts()需要在服务工作者代码的初始启动执行期间调用,而不是在事件处理程序内部调用.即importScripts()不支持"懒加载" .
  • importScripts()按照列出的顺序,逐个地同步执行文件内的所有代码.您可以拥有多个importScripts()importScripts()在其自身导入的文件内部,并且它们都将按照定义的顺序执行.
  • 在导入的脚本中,self将设置为与ServiceWorkerGlobalScope代码位于顶级服务工作程序时使用的相同.即,调用self.addEventListener()导入脚本内部或顶层服务工作者内部没有区别.
  • (这与您的问题没有直接关系,但最好知道:) importScripts()默认情况下,引用的文件将被缓存,使用内置于浏览器中的相同机制来缓存您的顶级服务工作文件.虽然目前正在对服务工作者规范进行一些更改以进行更改,但截至目前,importScripts()只要文件名不变,这些缓存文件将无限期使用.因此,最佳做法是在引用的任何内容的文件名中包含版本号或哈希值importScripts().

多个获取事件处理程序

当您有多个电话时会发生什么self.addEventListener('fetch')

从上一节我们知道,这些多个调用是源自importScripts()资源还是顶级服务工作者,这无关紧要.它们都在相同的全球范围内运作.

行为是明确定义的:当客户端页面发出请求时,它将按照注册的顺序fetch逐个触发控制服务工作者的处理程序,直到第一次调用为止.一个事件处理程序调用,不会触发其他获取事件处理程序,并且该处理程序的唯一责任是(最终)将a返回到客户端页面.event.respondWith()fetchrespondWith()Response

由于您的self.addEventlistener('fetch')通话顺序很重要,请确保importScripts()以适当的顺序列出您的文件,并importScripts()在您fetch在顶级服务工作者中定义任何事件处理程序之前或之后包含您的调用,具体取决于您想要的优先考虑

虽然您可以使用条件逻辑来确定是否要调用event.respondWith(),但该逻辑不能是异步的,因为服务工作者不会等待查看是否event.respondWith()被调用.它需要同步移动到下一个event处理程序(假设有一个).

所以在fetch处理程序中,你可以使用条件逻辑

// This can be executed synchronously.
if (event.request.url.endsWith('.html')) {
  event.respondWith(...);
}
Run Code Online (Sandbox Code Playgroud)

但你不能使用条件逻辑,如:

// 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(...);
  }
});
Run Code Online (Sandbox Code Playgroud)

如果您希望自己查看多处理程序行为,可以使用实时代码示例.