服务工作进程更新,但仍缓存旧文件



当我更新服务工作线程时,我关闭并重新打开页面,并得到控制台日志,指出新的服务工作程序已安装并激活,并且旧缓存已被删除。但是,列出的要添加到缓存中的文件不会再次从服务器获取,它们是几小时前的过时版本。除了清除Chrome开发工具中的存储空间外,无法使其更新文件。

如何让我的服务工作进程在安装新版本时使用新文件填充其缓存?

例如,当服务工作者更新时,"stylesheet.css"仍然是旧版本:

let cacheName = "abc_web_v1_15";
self.addEventListener('install', (event) => {
console.log('[Service Worker] Install');
event.waitUntil(
caches.open(cacheName).then(function (cache) {
return cache.addAll([
"/".
"/stylesheet.css",
...
]);
})
);
});
self.addEventListener('fetch', (e) => {
e.respondWith(
caches.match(e.request).then(function (r) {
console.log('[Service Worker] Fetching resource: ' + cacheName + ' ' + e.request.url);
if (r) {
console.log(cacheName + ' returning cached  ' + e.request);
return r;
} else {
console.log(cacheName + ' returning fetched  ' + e.request);
return fetch(e.request);
}
})
)
});
self.addEventListener('activate', (e) => {
console.log('[Service Worker] Activate');
e.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(keyList.map((key) => {
if (cacheName.indexOf(key) === -1) {
console.log(`[Service Worker] Removing Cached Files from Cach-${key}`);
return caches.delete(key);
}
}));
})
);
});

问题很简单:当您的服务工作者请求"新"文件时,该fetch请求会通过常规浏览器 HTTP 缓存。如果在那里找到资源,浏览器会自动返回该资源,并且不会进入网络。

如果您查看代码中的以下代码片段:

return cache.addAll([
"/".
"/stylesheet.css",
...
]);

您将看到无论内容如何,样式表的名称始终相同。换句话说,当您的样式表更新时,文件名仍然相同。因此,该文件已经在浏览器的HTTP缓存中,因此SW在请求时会获取它。

您应该对文件进行版本控制。通常使用文件内容的哈希。使用哈希命名,您最终会得到像stylesheet.iudfirio132.css这样的名称,并且每次文件更改一个字符,该名称都会更改。这样,新版本的应用程序始终使用具有不同名称的资源。您还应该使用工作框预缓存等生成工具来更新 Service Worker 文件中的资源列表,这样就无需手动执行此操作。更多: https://developers.google.com/web/tools/workbox/modules/workbox-precaching

为了完全掌握这里的主题,我建议您阅读这篇博文,"缓存最佳实践和最大年龄陷阱"https://jakearchibald.com/2016/caching-best-practices/

当我需要清除所有旧的服务工作者和服务工作者缓存时,我会把这个自毁的服务工作者放在手边。

我发现它是一个非常有用的工具。

self.addEventListener('install', event => self.skipWaiting());
self.addEventListener('activate', event => {
// Delete all Service Worker Caches
caches.keys().then(cacheNames => {for (let name of cacheNames) {caches.delete(name);}});
// Unregister all Service Workers
self.registration.unregister()
.then(() => self.clients.matchAll())
.then((clients) => clients.forEach(client => client.navigate(client.url)))
});

延伸阅读:

  • https://medium.com/@nekrtemplar/self-destroying-serviceworker-73d62921d717
  • https://github.com/NekR/self-destroying-sw/blob/master/tests/service-worker/www_fixed/sw.js

我在这里回答了一个类似的问题:https://stackoverflow.com/a/64880568/7626299

正如 pate 所提到的,即使您使用新代码、新缓存名称、调用self.skipWaiting()更新服务工作者,他仍然将旧资源放入缓存中!

实际上,您可以重写安装事件处理程序并摆脱cache.addAll。要解决此问题,您需要通过将cache选项设置为reload来获取每个资源,以便浏览器将使用网络获取资源而不是缓存。

const req = new Request(url, { cache: 'reload' });

更多细节在这里。

最新更新