Javascript Service Worker 正在缓存文件,但从未触发 fetch 事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34147562/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Service worker is caching files but fetch event is never fired
提问by srhise
I have just attempted to implement service workers to cache some JSON files and other assets on a static site (running on localhost chrome Version 47.0.2526.73 (64-bit)). Using cache.addAll() I have added my files to the cache, and when I open the resources tab in chrome, and click on cache storage, all the files are listed.
我刚刚尝试实现服务工作者以在静态站点上缓存一些 JSON 文件和其他资产(在 localhost chrome 版本 47.0.2526.73(64 位)上运行)。使用 cache.addAll() 我已将我的文件添加到缓存中,当我在 chrome 中打开资源选项卡并单击缓存存储时,会列出所有文件。
The issue I am having is that my service worker is listed as "activated" and "running" in chrome://service-worker-internals however, I cannot determine if the worker is actually intercepting the requests and serving up the cached files. I have added the event listener and even when I console log the event in the service workers dev tools instance, it never hits the break point:
我遇到的问题是,我的服务工作者在 chrome://service-worker-internals 中被列为“已激活”和“正在运行”,但是,我无法确定该工作者是否实际上正在拦截请求并提供缓存文件。我已经添加了事件侦听器,即使我在 Service Workers 开发工具实例中控制台记录事件时,它也永远不会到达断点:
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() {
}));
});
Basically I am running through things exactly as described in HTML5 rocks service workers intro, but I am pretty sure that my assets aren't being served from the cache. I've noted that assets served up from a service worker are noted as such in the network tab of devtools in the size column by indicating 'from service workers'.
基本上,我完全按照 HTML5 岩石服务工作者介绍中描述的方式运行,但我很确定我的资产不是从缓存中提供的。我已经注意到,从服务工作者提供的资产在大小列中的 devtools 的网络选项卡中通过指示“来自服务工作者”来注明。
It just seems as if my code is no different than the examples but it's not ever hitting the fetch event for some reason. Gist of my code: https://gist.github.com/srhise/c2099b347f68b958884d
似乎我的代码与示例没有什么不同,但由于某种原因,它从未遇到过 fetch 事件。我的代码要点:https: //gist.github.com/srhise/c2099b347f68b958884d
回答by Stephen Archer
After looking at your gist and your question, I think your issue is with scoping.
在查看了您的要点和问题之后,我认为您的问题与范围界定有关。
From what I've determined with service workers(at least with static files), the service worker only has a maximum scope of the directory it is in. That is to say, it can't load files/requests/responses that are pulled from a location at or above its structure, only below.
根据我对服务工作者(至少对于静态文件)的确定,服务工作者只有其所在目录的最大范围。也就是说,它无法加载被拉取的文件/请求/响应从其结构处或上方的位置,仅在下方。
For example, /js/service-worker.js will only be able to load files in /js/{dirName}/.
例如,/js/service-worker.js 将只能加载 /js/{dirName}/ 中的文件。
Therefore, if you change the location of your service worker to the root of your web project, the fetch event should fire and your assets should load from cache.
因此,如果您将 Service Worker 的位置更改为 Web 项目的根目录,则应该触发 fetch 事件并且您的资产应该从缓存加载。
So something like /service-worker.js, which should be able to access the /json directory, since it is deeper than the service-worker.js file.
所以像 /service-worker.js 这样的东西,它应该能够访问 /json 目录,因为它比 service-worker.js 文件更深。
This is further explained here, in the "Register A service worker" section. https://developers.google.com/web/fundamentals/getting-started/primers/service-workers
这将在“注册服务工作者”部分进一步解释。https://developers.google.com/web/fundamentals/getting-started/primers/service-workers
回答by garviand
I struggled with this for a long time, and I think the documentation related to the matter is seriously lacking. In my experience, there is a very important distinction:
我为此苦苦挣扎了很长时间,我认为与此事相关的文档严重缺乏。根据我的经验,有一个非常重要的区别:
The service worker can only intercept fetch events if it is in or above the scope of the URL it is accessed from.
Service Worker 只能拦截 fetch 事件,如果它在它被访问的URL的范围内或之上。
For example, my sw.js file was located at /static/sw.js
. When accessing my site's root at /
and attempting to intercept fetch events to js files in /static/js/common.js
, the fetch events were not intercepted, even though the scope of my service worker was /static/
and the js file was in /static/js/
.
例如,我的 sw.js 文件位于/static/sw.js
. 当访问我的网站的根目录/
,并试图拦截获取事件的js文件中/static/js/common.js
,抓取事件无法拦截,即使我的服务人员的范围是/static/
和js文件是/static/js/
。
Once I moved my sw.js file to the top-level scope /sw.js
, the fetch events were all intercepted. This is because the scope of the page I was accessing with my browser /
was the same as the scope of my sw.js file /
.
一旦我将我的 sw.js 文件移动到顶级 scope /sw.js
,所有 fetch 事件都被拦截了。这是因为我使用浏览器访问的页面/
范围与我的 sw.js 文件的范围相同/
。
Please let me know if this clears things up for people, or if I am incorrect!
请让我知道这是否为人们解决了问题,或者我是否不正确!
回答by Kinlan
The exact code in the HTML5Rocks article is
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;
}
);
})
);
});
The biggest thing that I can see is that you are not cloning the request
from the fetch, you need to clone it because it is read twice, once when being used to access the network (in the fetch
) and once when being used as the key to the cache.
我能看到的最重要的事情是你没有request
从 fetch 中克隆它,你需要克隆它,因为它被读取了两次,一次用于访问网络(在 中fetch
),一次用作密钥时缓存。
回答by Salva
If you don't see the mark from service worker
then your page is not controlledby the service worker yet (you can check by inspecting navigator.serviceWorker.controller
in the client page). The default behaviour for a page is to become controlled the next time you visit it after SW activation so you have two options:
如果您没有看到该标记,from service worker
那么您的页面尚未由 Service Worker控制(您可以通过navigator.serviceWorker.controller
在客户端页面中检查来进行检查)。页面的默认行为是在 SW 激活后下次访问它时受到控制,因此您有两个选择:
- Refresh the page after registration.
- Use
self.skipWaiting()
andself.clients.claim()
methods during installation and activation respectively to force the service worker to take control of the clients ASAP.
- 注册后刷新页面。
- 分别在安装和激活过程中使用
self.skipWaiting()
和self.clients.claim()
方法强制 service worker 尽快控制客户端。
Look at the Service Worker Cookbook, it includes an JSON cache recipe.
查看Service Worker Cookbook,它包含一个JSON 缓存配方。
Once you fix the problem with control, you need to fix your handler. I think you want to apply a policy of cache-first. If not in cache, then go to network and fill the cache. To do that:
一旦您解决了控制问题,您就需要修复您的处理程序。我认为您想应用缓存优先策略。如果不在缓存中,则转到网络并填充缓存。要做到这一点:
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;
});
});
});
}