Service Worker 正在缓存文件,但从未触发 fetch 事件

Service worker is caching files but fetch event is never fired

我刚刚尝试实施服务工作者以在静态站点上缓存一些 JSON 文件和其他资产(运行 在本地主机上 chrome 版本 47.0.2526.73(64 位)).使用 cache.addAll() 我已将我的文件添加到缓存中,当我在 chrome 中打开资源选项卡并单击缓存存储时,会列出所有文件。

我遇到的问题是我的服务人员在 chrome://service-worker-internals 中被列为 "activated" 和 "running" 但是,我无法确定是否worker 实际上是在拦截请求并提供缓存文件。我已经添加了事件侦听器,即使我在服务工作者开发工具实例中控制台记录事件,它也从未达到断点:

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      console.log(cache);
      return cache.addAll([
        '/json/0.json',
        '/json/1.json',
        '/json/3.json',
        '/json/4.json',
        '/json/5.json',
      ]);
    })
  );
});

this.addEventListener('fetch', function(event) {
  console.log(event);
  var response;
  event.respondWith(caches.match(event.request).catch(function() {
    return fetch(event.request);
  }).then(function(r) {
    response = r;
    caches.open('v1').then(function(cache) {
      cache.put(event.request, response);
    });
    return response.clone();
  }).catch(function() {
  }));
});

基本上,我 运行 完全按照 HTML5 rocks service workers 介绍中描述的方式进行操作,但我很确定我的资产不是从缓存中获取的。我注意到,通过指示 'from service workers'.

,服务工作者提供的资产在 devtools 的网络选项卡中的大小列中被注明

我的代码似乎与示例没有什么不同,但由于某种原因它从未触发过获取事件。我的代码要点:https://gist.github.com/srhise/c2099b347f68b958884d

HTML5Rocks 文章中的确切代码是

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        // IMPORTANT: Clone the request. A request is a stream and
        // can only be consumed once. Since we are consuming this
        // once by cache and once by the browser for fetch, we need
        // to clone the response
        var fetchRequest = event.request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have 2 stream.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});

我能看到的最重要的事情是你没有从 fetch 中克隆 request,你需要克隆它,因为它被读取了两次,一次是在用于访问网络时(在fetch) 和一次用作缓存的键时。

如果你没有看到标记 from service worker 那么你的页面还没有被 service worker 控制(你可以通过检查 navigator.serviceWorker.controller在客户端页面)。页面的默认行为是在软件激活后下次访问它时受到控制,因此您有两个选择:

  • 注册后刷新页面。
  • 分别在安装和激活过程中使用self.skipWaiting()self.clients.claim()方法强制service worker尽快控制客户端。

看看Service Worker Cookbook, it includes an JSON cache recipe

解决控件问题后,您需要修复处理程序。我认为您想应用 cache-first 的策略。如果不在缓存中,则转到网络并填充缓存。为此:

self.onfetch = function (event) {
  var req = event.request;
  return event.respondWith(function cacheFirst() {
    // Open your cache.
    return self.caches.open('v1').then(function (cache) {
      // Check if the request is in there.
      return cache.match(req).then(function (res) {
        // If not match, there is no rejection but an undefined response.
        if (!res) {
          // Go to network.
          return fetch(req.clone()).then(function (res) {
            // Put in cache and return the network response.
            return cache.put(req, res.clone()).then(function () {
              return res;
            });
          });
        }
        return res;
      });
    });
  });
}

在查看了您的要点和问题后,我认为您的问题与范围界定有关。

根据我对service worker的判断(至少是静态文件),service worker只有它所在目录的最大范围。也就是说,它无法加载files/requests/responses 从其结构或上方的位置拉出,仅在下方。

例如,/js/service-worker.js 将只能加载 /js/{dirName}/.

中的文件

因此,如果您将 Service Worker 的位置更改为 Web 项目的根目录,应该会触发 fetch 事件并且应该从缓存中加载您的资产。

所以像 /service-worker.js 这样的东西应该可以访问 /json 目录,因为它比 service-worker.js 文件更深。

这在 "Register A service worker" 部分中有进一步解释。 https://developers.google.com/web/fundamentals/getting-started/primers/service-workers

我为此纠结了很长时间,我认为与此事相关的文档严重缺乏。根据我的经验,有一个非常重要的区别:

Service Worker 只有在 URL 的范围内或之上才能拦截获取事件,它是从 .

访问的

例如,我的 sw.js 文件位于 /static/sw.js。当在 / 访问我网站的根目录并试图拦截对 /static/js/common.js 中 js 文件的抓取事件时,抓取事件没有被拦截,即使我的 service worker 的范围是 /static/ 并且js 文件位于 /static/js/.

一旦我将我的 sw.js 文件移动到顶级范围 /sw.js,所有获取事件都被拦截了。这是因为我使用浏览器 / 访问的页面范围与我的 sw.js 文件 /.

的范围相同

请让我知道这是否为人们解决了问题,或者我是否不正确!

当我将 sw.js 与 Django Framework 一起使用时,我遇到了同样的问题。 Here 我找到了解决方案。