【问题标题】:Service worker is caching files but fetch event is never firedService Worker 正在缓存文件,但从未触发 fetch 事件
【发布时间】:2016-03-12 21:09:34
【问题描述】:

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

我遇到的问题是我的服务工作者在 chrome://service-worker-internals 中被列为“已激活”和“正在运行”,但是,我无法确定该工作者是否真的在拦截请求并提供服务缓存的文件。我已经添加了事件监听器,即使我在服务工作者开发工具实例中控制台记录事件,它也永远不会到达断点:

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 简介中的描述进行操作,但我很确定我的资产不是从缓存中提供的。我注意到从服务工作者提供的资产在大小列的 devtools 的网络选项卡中通过指示“来自服务工作者”来注明。

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

【问题讨论】:

  • 您找到解决方案了吗?
  • 我认为@Stephen Archers 的提示应该被标记为正确。
  • 是的,您应该将@Stephen Archers 的答案标记为正确。

标签: javascript google-chrome service-worker progressive-web-apps


【解决方案1】:

当我将 sw.js 与 Django 框架一起使用时,我遇到了同样的问题。 Here我找到了解决办法。

【讨论】:

    【解决方案2】:

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

    Service Worker 只能在它位于或高于它所访问的 URL 的范围内时拦截 fetch 事件

    例如,我的 sw.js 文件位于 /static/sw.js。在/ 访问我网站的根目录并尝试拦截/static/js/common.js 中的js 文件的提取事件时,即使我的服务工作者的范围是/static/ 并且js 文件在@987654325 中,也没有拦截到提取事件@。

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

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

    【讨论】:

    • 这比其他解释更好。谢谢!
    • @garviand:如果我使用“allow-server-worker”标头作为响应,而不是将我的文件移动到根目录,会发生什么?
    【解决方案3】:

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

    根据我对服务工作者(至少对于静态文件)的确定,服务工作者只有它所在目录的最大范围。也就是说,它无法加载文件/请求/响应从其结构上或上方的位置拉出,仅在下方。

    例如,/js/service-worker.js 将只能加载 /js/{dirName}/ 中的文件。

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

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

    这在此处的“注册服务工作者”部分中有进一步的解释。 https://developers.google.com/web/fundamentals/getting-started/primers/service-workers

    【讨论】:

    • 斯蒂芬的答案应该被标记为正确。我刚刚遇到了同样的问题,因为我将 web-worker.js 存储在子文件夹 /assets/js/ 中。在存储文件并注册网络工作者时,它无法获取获取请求。在我将它移动到该域的根目录后,它工作正常。
    • 服务人员是否可以访问自己目录中的文件?即app/service-worker.js,它可以读取app/offline-page.html吗?
    • @Prefix 可以,应该可以。
    • 如果serviceworker不在根目录我也遇到了同样的问题,serviceworker真的可以定位到子文件夹吗?
    • 谢谢。也为我工作!
    【解决方案4】:

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

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

    查看Service Worker Cookbook,它包含一个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;
          });
        });
      });
    }
    

    【讨论】:

    • 我从未见过使用return self.caches.open('v1').then(function (cache) {这样的实现,你能给我这个appraich的链接吗
    【解决方案5】:

    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 中)和一次用作缓存的键时。

    【讨论】:

      猜你喜欢
      • 2018-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-28
      • 1970-01-01
      • 2017-12-26
      • 1970-01-01
      • 2016-10-28
      相关资源
      最近更新 更多