Service Worker更新,但旧文件仍被缓存

3

当我更新服务工作线程时,关闭和重新打开页面后,控制台会记录新的服务工作线程已被安装和激活,并且旧缓存已被删除。然而,需要添加到缓存中的文件并没有从服务器重新获取,它们是几个小时前的过期版本。除了在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);
                }
            }));
        })
    );
});

我在这里回答了一个类似的问题,希望它能对你有所帮助:https://dev59.com/d1wX5IYBdhLWcg3w4S3R#64880568 - adrienv1520
我在这里回答了一个类似的问题,希望对你有所帮助:https://dev59.com/d1wX5IYBdhLWcg3w4S3R#64880568 - adrienv1520
3个回答

5
问题很简单:当你的Service Worker请求“新”文件时,fetch请求会通过普通浏览器HTTP缓存。如果资源在那里找到,浏览器自动返回它,不会访问网络。
如果你看一下代码片段:
return cache.addAll([
                "/".
                "/stylesheet.css",
                ...
            ]);

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

您应该对文件进行版本控制。通常使用文件内容的哈希值。通过哈希命名,您最终得到像 stylesheet.iudfirio132.css 这样的名称,并且每次文件更改时,甚至只更改一个字符,该名称都会发生变化。这样,应用程序的新版本始终使用具有不同名称的资源。您还应该具备构建工具,例如Workbox预加载工具,以更新Service Worker文件中的资源列表,以便您无需手动执行此操作。更多信息: https://developers.google.com/web/tools/workbox/modules/workbox-precaching

为了完全掌握这个主题,我建议您阅读这篇博客文章,“缓存最佳实践和max-age陷阱”https://jakearchibald.com/2016/caching-best-practices/


1
我会把这个自毁的 Service Worker 放在手边,以便在需要清除所有旧的 Service Worker 和 Service Worker 缓存时使用。
我发现它是一个非常有用的工具。
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)))

});

更多阅读:

这样做不会无限制地重新加载页面吗? - Willie
1
我明白你的意思。我一直在忙于寻找window.location.reload的其他解决方案,以至于我的大脑把client.navigate变成了重新加载...谢谢! - Willie
对我来说,这导致应用程序在服务工作者被激活后无限循环刷新... - Willie

0

我在这里回答了一个类似的问题: https://dev59.com/d1wX5IYBdhLWcg3w4S3R#64880568

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

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

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

更多细节请点击这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接