【问题标题】:Sharing fetch handler logic defined across multiple service workers共享跨多个服务工作者定义的获取处理程序逻辑
【发布时间】:2017-12-28 16:22:58
【问题描述】:

关注This Discussion,哪里有评论说

修补 Fetch

通过覆盖 self.fetch、self.XMLHttpRequest 和 self.caches(对于 缓存.add/addAll)?看起来这些可以让您在主 SW 脚本看到它们之前拦截网络请求并操纵响应。

我一直在寻找有关此类内容的任何文档,但似乎找不到任何文档。

在我只是需要 multiple 服务工作者共同存在于单个范围内的场景中,

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

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

【问题讨论】:

    标签: javascript service-worker offline-caching


    【解决方案1】:

    这里有几件事要介绍:

    单一范围内的多个 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-07
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 2013-03-18
      • 1970-01-01
      • 2019-11-19
      • 1970-01-01
      相关资源
      最近更新 更多