服务工作者可以获取和缓存跨域资产吗?

阿绍利

我正在使用Google渐进式Web应用程序教程中的一些服务人员代码,但出现错误:

未捕获(承诺)TypeError:
 无法对“ Response”执行“ clone”:
 响应主体已被使用

该站点将第三方Javascript和样式表用于Web字体。我想将托管在这些CDN上的资产添加到脱机缓存中。

addEventListener("fetch", function(e) {
  e.respondWith(
    caches.match(e.request).then(function(response) {
        return response || fetch(e.request).then(function(response) {
        var hosts = [
          "https://fonts.googleapis.com",
          "https://maxcdn.bootstrapcdn.com",
          "https://cdnjs.cloudflare.com"
        ];
        hosts.map(function(host) {
          if (e.request.url.indexOf(host) === 0) {
            caches.open(CACHE_NAME).then(function(cache) {
              cache.put(e.request, response.clone());
            });
          }
        });
        return response;
      });
    })
  );
});

这些托管在流行的CDN上,因此我的直觉是它们应该为CORS标头做正确的事情。

以下是我要缓存的HTML中的资产:

<link rel="stylesheet" type="text/css"
      href="https://fonts.googleapis.com/css?family=Merriweather:900,900italic,300,300italic">
<link rel="stylesheet" type="text/css"
      href="https://fonts.googleapis.com/css?family=Lato:900,300" rel="stylesheet">
<link rel="stylesheet" type="text/css"
      href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css">
<script type="text/javascript" async
        src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>

根据控制台日志,服务工作者正在尝试获取以下资产:

获取完成的加载:
 获取“ https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css”。
 sw.js:32
提取完成的加载:
 GET“ https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML”。
 sw.js:32
获取完成的加载:
 GET“ https://fonts.googleapis.com/css?family=Merriweather:900,900italic,300,300italic”。
 sw.js:32
获取完成的加载:
 GET“ https://fonts.googleapis.com/css?family=Lato:900,300”。
 sw.js:32
提取完成的加载:
 GET“ https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/config/TeX-AMS-MML_HTMLorMML.js?V=2.7.1”。
 sw.js:32
获取完成的加载:
 获取“ https://maxcdn.bootstrapcdn.com/font-awesome/latest/fonts/fontawesome-webfont.woff2?v=4.7.0”。
 sw.js:32

如果我删除了clone,那么为什么必须在服务工作者中克隆获取请求中建议,我将得到相同的错误:

TypeError:响应主体已被使用

如果将{ mode: "no-cors" }每个Service worker CORS问题添加到访存中,我将得到相同的错误和以下警告:


 “ https://maxcdn.bootstrapcdn.com/font-awesome/latest/fonts/fontawesome-webfont.woff2?v=4.7.0”的FetchEvent
 导致网络错误响应:“不透明”响应
 用于类型不是no-cors
的请求
 “ https://fonts.gstatic.com/s/lato/v14/S6u9w4BMUTPHh50XSwiPGQ3q5d0.woff2”的FetchEvent
 导致网络错误响应:“不透明”响应
 用于其请求的请求类型不是no-cors 
 “ https://fonts.gstatic.com/s/lato/v14/S6u9w4BMUTPHh7USSwiPGQ3q5d0.woff2”
的FetchEvent
 导致网络错误响应:类型为“ opaque”的响应
 用于请求不是无心
的FetchEvent
 “ https://fonts.gstatic.com/s/merriweather/v19/u-4n0qyriQwlOrhSvowK_l521wRZWMf6hPvhPQ.woff2”
 导致网络错误响应:
 对于类型不是no-cors的请求,使用了“不透明”响应

我可以在服务工作者的install事件中将这些资产添加到静态缓存中,但是我有理由仅在fetch事件中将它们添加到缓存中

杰夫·波斯尼克

使用clone()您走在正确的轨道上,但是时间很重要。您需要确保clone()在最终return response执行之前调用,因为这时,响应将传递到服务工作者的客户端页面,并且其主体将被“消耗”。

解决此问题的方法有两种:要么clone()在执行异步缓存代码之前调用要么将您的return response语句延迟到缓存完成之后。

我将建议第一种方法,因为这意味着您最终将尽快获得对页面的响应。我还建议您使用async/await重写代码,因为它更具可读性(并且受今天也支持服务工作者的任何浏览器的支持)。

addEventListener("fetch", function(e) {
  e.respondWith((async function() {
    const cachedResponse = await caches.match(e.request);
    if (cachedResponse) {
      return cachedResponse;
    }

    const networkResponse = await fetch(e.request);

    const hosts = [
      'https://fonts.googleapis.com',
      'https://maxcdn.bootstrapcdn.com',
      'https://cdnjs.cloudflare.com',
    ];

    if (hosts.some((host) => e.request.url.startsWith(host))) {
      // This clone() happens before `return networkResponse` 
      const clonedResponse = networkResponse.clone();

      e.waitUntil((async function() {
        const cache = await caches.open(CACHE_NAME);
        // This will be called after `return networkResponse`
        // so make sure you already have the clone!
        await cache.put(e.request, clonedResponse);
      })());
    }

    return networkResponse;
  })());
});

注意:(async function() {})()语法可能看起来有些怪异,但这是在立即执行的函数中使用async/的快捷方式,await该函数将返回promise。请参阅http://2ality.com/2016/10/async-function-tips.html#immediately-invoked-async-function-expressions

对于原始代码,需要在执行异步缓存更新之前克隆响应:

        var clonedResponse = response.clone();
        caches.open(CACHE_NAME).then(function(cache) {
          cache.put(e.request, clonedResponse);
        });

GoogleService Worker入门提供了示例代码,显示了正确的方法。该代码带有带“重要”注释的注释,但它只是强调克隆,而不是克隆时遇到的问题:

        // 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 two streams.
        var responseToCache = response.clone();

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

        return response;

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何卸载服务工作者?

Javascript服务工作者:从缓存中获取资源,但也进行更新

是否可以修改服务工作者缓存响应标头?

服务工作者和客户端请求“缓存控制”:“无缓存”

WebView:以编程方式清除服务工作者缓存

使用CSRF令牌缓存请求时,如何减少服务工作者缓存的大小

具有SSL证书缓存的服务工作者

PWA服务工作者缓存/更新预期行为

服务工作者获取事件未触发

notificationclick事件服务工作者

将服务工作者保存在缓存中有意义吗?

根据年龄删除服务工作者缓存中的条目

用户在网站上时,可以使用服务工作者显示通知吗?

注销时如何清除服务工作者缓存?

服务工作者和其他域(aws s3)的缓存图像:“将不缓存不透明的响应”

在服务工作者中服务之前,可以编辑缓存的index.html吗?

如何防止服务工作者删除缓存,PWA

如何由服务工作者更新缓存的资源

服务工作者可以通过某种方式知道请求页面的网址吗?

与服务工作者兼容的Azure通知中心吗?

服务工作者中的XMLHttpRequest

嵌套获取服务工作者中的响应

如何使用服务工作者预缓存名称在每次部署时都会更改的资产?

持久服务工作者和缓存 HTML 和 PNG 文件

服务工作者从磁盘缓存中缓存资源

服务工作者在反应中缓存的路由

服务工作者缓存与 VuexPersistence

角度缓存 - 我们应该使用服务工作者吗?

服务工作者缓存破坏谷歌地图 api