只在离线时使用ServiceWorker缓存

29

我正在尝试将服务工作者集成到我的应用程序中,但我发现即使在在线状态下,服务工作者也会尝试检索缓存的内容,但我希望在这些情况下它优先使用网络。我该怎么做?以下是我现在拥有的代码,但我不认为它正在起作用。为了简洁起见,SW安装代码被省略。

var CACHE_NAME = 'my-cache-v1';
var urlsToCache = [
  /* my cached file list */
];

self.addEventListener('install', function(event) {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

/* request is being made */
self.addEventListener('fetch', function(event) {
  event.respondWith(
    //first try to run the request normally
    fetch(event.request).catch(function() {
      //catch errors by attempting to match in cache
      return caches.match(event.request).then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
      });
    })
  );
});

这似乎会导致类似于 The FetchEvent for "[url]" resulted in a network error response: an object that was not a Response was passed to respondWith(). 的警告。我对服务工作者不太熟悉,因此对任何错误术语或不良实践表示歉意,欢迎任何建议。谢谢!


1
警告只发生在离线时吗?你可能会收到针对意外URL的请求,比如favicon,这些请求并未存储在缓存安装事件中。 - Ben Kelly
这种情况发生在在线时,而离线时Service Worker似乎按预期工作。 - Ruben Martinez Jr.
我也在离线时遇到了这些错误,但你是对的,只有对于我没有存储在缓存中的资源才会出现这种情况。 - Ruben Martinez Jr.
@RubenMartinezJr. 你解决了这个问题吗?我在我的Rails设置中也遇到了同样的问题。非常奇怪。 - Jody Heavener
我其实没有用那个 :/ 我改用了谷歌的 SW-Toolbox,它的API更好用! - Ruben Martinez Jr.
3个回答

17

没有测试过,我猜测你在没有缓存匹配的情况下没有正确解析respondWith()。根据MDN的说明,传递给respondWith()的代码应该“通过返回响应或网络错误来解析Fetch。”那么为什么不尝试这样做:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request).catch(function() {
      return caches.match(event.request);
    })
  );
});

4
请参阅Jake的离线手册:https://jakearchibald.com/2014/offline-cookbook/#network-falling-back-to-cache - Ben Kelly
2
这段代码本质上与我上面的代码基本相同,不幸的是会导致相同的错误 :/ - Ruben Martinez Jr.
2
鉴于这似乎是记录下来的方法,我会接受这个并假设某处存在一些可疑的事情……如果我发现更多信息,会进行更新。 - Ruben Martinez Jr.
只是一个想法,但你确定文件名/网址是正确的吗?我正在测试类似的东西,它似乎没有被捕获,然后你会得到一个未定义的响应对象,这会导致此错误。 - Aries51
1
另一个快速想法:由于您收到了“respondWith”警告,这意味着“caches.match()”未找到该资源。可能只是查询字符串或尾随斜杠的问题。 - GreatBlakes
在缓存未命中的情况下(返回未定义),那样是行不通的。你总是需要返回一个可以合成的响应。 - Kalle

6
为什么不为您的fetch事件打开缓存? 我认为service worker的过程是:
- 打开您的缓存 - 检查请求是否与缓存中的答案匹配 - 然后回答
或者(如果答案不在缓存中):
- 通过网络检查请求 - 从网络克隆您的答案 - 将请求和答案的克隆放入缓存以供将来使用
我会写成:
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(CACHE_NAME).then(cache => {
     return cache.match(event.request).then(response => {
      return response || fetch(event.request)
      .then(response => {
        const responseClone = response.clone();
        cache.put(event.request, responseClone);
        })
      })
    }
 );
});

5

event.respondWith() 期望一个解析为 Response 的 Promise。因此,如果缓存未命中,您仍需要返回一个 Response,但是在上面的代码中,您没有返回任何内容。我建议您先使用缓存,然后再进行网络请求,但无论如何,作为最后的手段,您总是可以创建一个合成的 Response,例如以下示例:

return new Response("Network error happened", {"status" : 408, "headers" : {"Content-Type" : "text/plain"}});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接