仅在离线时使用 ServiceWorker 缓存

Posted

技术标签:

【中文标题】仅在离线时使用 ServiceWorker 缓存【英文标题】:Use ServiceWorker cache only when offline 【发布时间】:2016-01-07 19:55:45 【问题描述】:

我正在尝试将 Service Worker 集成到我的应用程序中,但我发现 Service Worker 即使在在线时也会尝试检索缓存的内容,但我希望它在这些情况下更喜欢网络。我怎样才能做到这一点?下面是我现在拥有的代码,但我不相信它正在工作。为简洁起见,省略了 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(). 这样的警告,我是服务人员的新手,因此对任何错误的术语或不良做法表示歉意,欢迎任何提示。谢谢!

【问题讨论】:

警告是否仅在离线时出现?您可能会收到对未存储在缓存安装事件中的意外 URL 的请求,例如网站图标。 这发生在联机时,脱机时服务人员实际上似乎按预期工作。 编辑:离线时我也会收到这些错误,但你是对的,仅适用于我没有存储在缓存中的资源。 @RubenMartinezJr。你设法弄清楚了吗?我在 Rails 设置中遇到了同样的问题。很奇怪。 我真的没有:/ 我改用 Google 的 SW-Toolbox,它是一个更好的 API! 【参考方案1】:

如果不对此进行测试,我的猜测是在没有缓存匹配的情况下您没有正确解析respondWith()。 According to MDN,传递给respondWith() 的代码应该“通过将响应或网络错误返回给 Fetch 来解决”。那么为什么不尝试这样做呢:

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

【讨论】:

另见 Jake 的离线食谱:jakearchibald.com/2014/offline-cookbook/… 这段代码与我上面的代码基本相同,不幸的是导致同样的错误:/ 鉴于这似乎是记录在案的方法,我将接受这一点,并假设某处发生了一些可疑的事情......如果我发现更多,会更新。 只是一个想法,但您确定文件名/url 正确吗?我正在测试类似的东西,似乎没有达到捕获,然后你得到一个导致此错误的未定义响应对象。 另一个快速思考:既然您收到了respondWith 警告,这意味着caches.match() 没有找到该资源。可以像查询字符串或斜杠一样简单。【参考方案2】:

为什么不为 fetch 事件打开缓存? 我认为 service worker 的流程是:

打开缓存

检查请求是否与缓存中的答案匹配

那你回答

OR(如果答案不在缓存中):

通过网络检查请求

从网络中复制您的答案

将请求和答案的克隆放入缓存中以备将来使用

我会写:

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);
        )
      )
    
 );
);

【讨论】:

【参考方案3】:

event.respondWith() expects a promise that resolves to Response。因此,在缓存未命中的情况下,您仍然需要返回一个响应,但在上面,您什么也没有返回。我也尝试先使用缓存,然后获取,但无论如何,作为最后的手段,你总是可以创建一个合成响应,例如这样的:

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

【讨论】:

以上是关于仅在离线时使用 ServiceWorker 缓存的主要内容,如果未能解决你的问题,请参考以下文章

即使在离线时如何缓存网页以在我的 android 应用程序中使用

“Cache-Control: only-if-cached”在离线时失败,即使响应被缓存

503 正常(来自 serviceWorker)

通过服务人员离线显示谷歌地图。

Android Webview离线时加载缓存

“错误:此对象在离线缓存中不可用”仅在 Parse 后端出现几次