ash*_*ley 2 javascript cross-domain cors service-worker
我正在使用Google的Progressive Web 应用程序教程中的一些 Service Worker 代码, 但出现错误:
未捕获(承诺)类型错误: 无法对“响应”执行“克隆”: 响应正文已被使用
该站点使用第三方 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;
});
})
);
});
Run Code Online (Sandbox Code Playgroud)
这些托管在流行的 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>
Run Code Online (Sandbox Code Playgroud)
根据控制台日志,Service Worker 正在尝试获取这些资产:
获取完成加载: 获取“https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css”。 sw.js:32 获取完成加载: 获取“https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML”。 sw.js:32 获取完成加载: 获取“https://fonts.googleapis.com/css?family=Merriweather:900,900italic,300,300italic”。 sw.js:32 获取完成加载: 获取“https://fonts.googleapis.com/css?family=Lato:900,300”。 sw.js:32 获取完成加载: 获取“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,正如为什么必须在服务工作者中克隆获取请求中所建议的那样?,我会得到同样的错误:
类型错误:响应正文已被使用
如果我添加{ mode: "no-cors" }到每个Service worker CORS issue的 fetch 中,我会收到相同的错误和这些警告:
的 FetchEvent “https://maxcdn.bootstrapcdn.com/font-awesome/latest/fonts/fontawesome-webfont.woff2?v=4.7.0” 导致网络错误响应:“不透明”响应是 用于类型不是 no-cors 的请求 的 FetchEvent “https://fonts.gstatic.com/s/lato/v14/S6u9w4BMUTPHh50XSwiPGQ3q5d0.woff2” 导致网络错误响应:“不透明”响应是 用于类型不是 no-cors 的请求 的 FetchEvent “https://fonts.gstatic.com/s/lato/v14/S6u9w4BMUTPHh7USSwiPGQ3q5d0.woff2” 导致网络错误响应:“不透明”响应是 用于类型不是 no-cors 的请求 的 FetchEvent “https://fonts.gstatic.com/s/merriweather/v19/u-4n0qyriQwlOrhSvowK_l521wRZWMf6hPvhPQ.woff2” 导致网络错误响应:“不透明”响应是 用于类型不是 no-cors 的请求
我可以在 service worker 的install事件中将这些资产添加到静态缓存中,但我有理由只在fetch事件中将它们添加到缓存中。
您在使用 的正确轨道上clone(),但时机很重要。您需要确保clone()在最终return response执行之前调用,因为此时,响应将传递到服务工作者的客户端页面,并且其正文将被“消耗”。
有两种方法可以解决这个问题:要么clone()在执行异步缓存代码之前调用,要么将return response语句延迟到缓存完成之后。
我将建议第一种方法,因为这意味着您最终会尽快获得对页面的响应。我还建议您重写代码以使用async/await,因为它更具可读性(并且受到当今也支持 service worker 的任何浏览器的支持)。
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;
})());
});
Run Code Online (Sandbox Code Playgroud)
注意:(async function() {})()语法可能看起来有点奇怪,但它是在立即执行的函数中使用async/的快捷方式,await该函数将返回一个承诺。请参阅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);
});
Run Code Online (Sandbox Code Playgroud)
Google的Service 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;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6824 次 |
| 最近记录: |