ServiceWorker:没有 javascript 触发请求的 FetchEvent?

ServiceWorker: No FetchEvent for javascript triggered request?

我正在使用 Chrome 稳定版 46。

我有一个非常基本的网站。一个 button.onclick fetch('a') 和一个 <a> 打开 b 的标签。 url ab 都不存在,我打算在 service worker 中捕获它们,只是 return a new Response().

如果我点击获取 a 的按钮,服务人员不参与,我收到 404 错误。
但是,如果我单击转到 b 的 link,服务人员将获取并 return 响应。

问题:为什么 service worker 没有为 fetch('a') 请求获取任何 FetchEvent?

查看下面的文件

HTML

<html>
  <body>
    <p><button onclick="fetch('a');">fetch a</button></p>
    <p><a href="b">go to b</a></p>
    <script src="js.js" defer></script>
  </body>
</html>

js.js

navigator.serviceWorker.register('sw.js');

sw.js

self.onfetch = function(event) {
  var request = event.request;
  event.respondWith(
    caches.match(request).then(function(response) {
      return new Response('here is your onfetch response');
    })
  );
};

Service Worker 的 fetch 事件处理程序在您第一次加载页面时未被调用,因为 Service Worker 尚未 "claimed" 页面,这意味着它不在 Service Worker 的控制。默认情况下,当您第一次访问注册了 service worker 的站点时,service worker 的 install(可能还有 activate)事件处理程序将 运行,但 fetch 不会在 service worker 取得控制权之前不会被触发。下次您导航到服务工作者范围内的页面时,或者如果您重新加载当前页面时,就会发生这种情况。

您可以通过在 activate 事件处理程序中调用 self.clients.claim() 来覆盖此默认行为并让服务工作者在初始导航期间进行控制。

我还想指出,您示例中的 fetch 事件处理程序存在一些问题 — 如果您总是计划 returning,则没有理由调用 caches.match(request)一个新的 Response 对象。更重要的是,您需要对 event.request.url 值进行某种检查,并且只有 return 新的 Response 如果它与您的 "special" URL 之一匹配,在您的情况下是 ab。现在的实现方式是,您的 fetch 处理程序将无条件地 return 虚拟 Response,即使它是对您的主页 (index.html) 的后续请求。这大概不是你想要的。

感谢 RawGit,我放了 gist together that modifies your service worker code to accomplish what I believe you're attempting. You can try out a live version。为了后代,这里是修改后的服务工作者代码:

self.addEventListener('install', event => {
  // Bypass the waiting lifecycle stage,
  // just in case there's an older version of this SW registration.
  event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate', event => {
  // Take control of all pages under this SW's scope immediately,
  // instead of waiting for reload/navigation.
  event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', event => {
  // In a real app, you'd use a more sophisticated URL check.
  if (event.request.url.match(/a|b$/)) {
    event.respondWith(new Response('here is your onfetch response'));
  }
});