飘在云端

啊!那蓝真天,白真云!

· 前端 · · 664次浏览

service-worker 服务端单方面强制刷新客户端(浏览器)缓存

服务端更新了文件,但是客户端浏览器还是旧的资源,并且还是直接从 service-worker 缓存中拉取,进一步测试,直接断网访问有 service-worker 缓存的网站,可以离线访问,F12 控制台可以看到浏览器直接读取本地旧的 service-worker 缓存。
如果用户是 Ctrl + F5 访问,使用 Ctrl + F5 的确能访问到最新资源,但是,一旦使用正常访问,就又会直接读取 service-worker 缓存,普通的 F5 刷新,依旧会读取本地旧的 service-worker 缓存,我们显然不可能要求每个用户去主动清除浏览器缓存。
如何让客户端浏览器在访问时主动刷新所有service-worker 缓存,修改相关的 service-worker.js 文件,在 waiting 阶段前面,增加如下行强制清空所有 Service Worker 缓存,同时自卸载所有 Service Worker 即可。

测试效果:使用任意已经缓存了旧版资源的 Service Worker 浏览器,正常访问变更了新资源的站点,无需 Ctrl + F5,此过程对用户是完全透明无感知的,全静默后台清除客户端浏览器本地的 Service Worker 缓存,同时强制访问服务端拉取资源。
移动端 Google WebView v93.x 测试,也成功在访问时自动刷新所有资源,客户端无需任何操作,只需要一次访问即可触发刷新。

self.addEventListener('install', event => self.skipWaiting());

self.addEventListener('activate', event => {

  // 删除所有 Service Worker 缓存
  caches.keys().then(cacheNames => {for (let name of cacheNames) {caches.delete(name);}});

  // 卸载所有 Service Worker
  self.registration.unregister()

    .then(() => self.clients.matchAll())

    .then((clients) => clients.forEach(client => client.navigate(client.url)))

});

查看控制台,返回如下:

Service worker has been registered.
registerServiceWorker.js:20 New content is downloading.
registerServiceWorker.js:17 Content has been cached for offline use.

如此修改后,Service worker 缓存将会永久失效,离线访问功能也不可用,这个是可以鱼与熊掌兼得的,更改另一个路由策略 cache-falling-back-to-network

  /*优先使用网络模式:
先请求服务端,检查服务端和本地缓存资源新旧,把服务端发生更新的资源,更新到本地缓存,比较一致未更新的部分直接使用本地缓存,如果网络不可用,就使用本地缓存,*/
 self.addEventListener('fetch', function (event) {
  event.respondWith(
    fetch(event.request).catch(function () {
      return caches.match(event.request);
    }),
  );
});
https://developers.google.com/web/tools/workbox/modules/workbox-precaching
https://github.com/NekR/self-destroying-sw/blob/master/tests/service-worker/www_fixed/sw.js
https://medium.com/@nekrtemplar/self-destroying-serviceworker-73d62921d717
https://web.dev/offline-cookbook/#cache-falling-back-to-network