乐闻世界logo
搜索文章和话题

How to force service worker to update?

8 个月前提问
5 个月前修改
浏览次数82

8个答案

1
2
3
4
5
6
7
8

在强制刷新或更新Service Worker时,通常涉及以下几个步骤:

  1. 更新Service Worker文件: 确保你已经对Service Worker脚本做了修改。即便是很小的改动,如更新文件中的注释,也能触发Service Worker的更新事件,因为浏览器会检查Service Worker文件是否有字节级别的变化。

  2. Service Worker生命周期利用: Service Worker的生命周期中有一个install事件。当检测到Service Worker文件有更新时,新的Service Worker将会进入install阶段。在这个阶段,你可以清除旧缓存并且执行新的缓存逻辑。

  3. 激活新的Service Worker: 在新的Service Worker安装后,它会进入wait状态。你可以通过调用self.skipWaiting()方法来强制当前正在等待的Service Worker立即进入activate状态。

  4. 更新客户端: 新的Service Worker激活后,并不会控制当前打开的页面,直到下次用户访问。为了让新的Service Worker立即接管,可以使用clients.claim()方法。

  5. 通知用户: 如果你希望用户知道有一个新版本可用,并且鼓励他们刷新页面来使用新的Service Worker,可以在页面上显示一个通知或者按钮来提示用户。

示例代码

以下是一个简单的Service Worker更新流程实例:

javascript
// 在Service Worker文件中 self.addEventListener('install', event => { // 强制当前正在等待的Service Worker立即进入activate状态 self.skipWaiting(); }); self.addEventListener('activate', event => { // 让新的Service Worker立即接管当前页面 event.waitUntil(clients.claim()); // 清理旧版本缓存的逻辑 });
javascript
// 在注册Service Worker的主文件中 if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js').then(reg => { reg.addEventListener('updatefound', () => { const newWorker = reg.installing; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // 新的Service Worker已经就绪,可以通知用户刷新页面了 // 这里可以显示一个“更新可用”的通知或者刷新按钮 } }); }); }); }

强制刷新页面

如果你控制了页面逻辑,你甚至可以在新的Service Worker激活后自动刷新页面,但这通常不是一个好的做法,因为用户可能会丢失未保存的状态。

你可以这样做:

javascript
if ('serviceWorker' in navigator) { navigator.serviceWorker.addEventListener('controllerchange', () => { // 当新的Service Worker取得控制权时,刷新页面 window.location.reload(); }); }

请注意,强制刷新页面可能导致用户体验问题,因此确保在适当的时机执行该操作,例如,当用户完成他们的工作并且页面可以安全地刷新时。

这些步骤和代码示例展示了如何在更新Service Worker时保持页面的正常运作并及时地将更新推送给用户。在实际应用中,会根据具体的业务需求和更新策略来调整这个流程。

2024年6月29日 12:07 回复

As the previous answers stated, there is a 1 day (24hr) built-in limit for the browser to look for updates to the service worker, but what you need to be mindful of is you still need to call .update() on your ServiceWorkerRegistration object otherwise the original version of the file will be used.

While testing changes in Chrome, I always right-click on the refresh button and select 'Empty Cache and Hard Reset' (options are available when developer tools are open) when working on my service worker script.

2024年6月29日 12:07 回复

'sw-precache' has been superseded by Workbox which generates hashes for the files you want to precache and adds those to the source code of your service worker (I don't know if sw-precache works the same way, Jeff's answer suggests it does). If any of the files you want to precache change -- and you re-run your build -- the updated hashes in your service worker are updated which pretty much guarantees the service worker will be "at least one byte different".

When you reload (ctrl-r) or re-visit your app the browser will fetch again the service worker that was registered for it. And, if that service worker has changed, it will be "queued up" to manage the app. But it won't start managing the app until you close all tabs with the app open, and then open a new one.

When you hard refresh (shift-ctrl-r) it’ll always load without a controller which is not the same as using your new service worker.

For testing during development, I use skip waiting in devtools to make the new service worker replace the old one without waiting for me to close all the tabs.

2024年6月29日 12:07 回复

If one file's hash changes, that's enough to ensure that the service worker JavaScript file generated by sw-precache changes. That, in turn, is enough to trigger the service worker update flow.

If you're not seeing an update flow triggered while testing things out, then it may be due to the service worker JavaScript file being served with HTTP caching headers that cause it to be fetched from the browser's cache rather than the from the network.

See https://stackoverflow.com/a/38854905/385997 for a discussion of how HTTP caching headers come into play, and the best practices with regards to that.

2024年6月29日 12:07 回复

One option that I have implemented from a UX standpoint is calling the unregister method on your service worker. (https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/unregister)

Once the unregister process is complete you can:

  1. force the window to refresh (window.location.reload)
  2. Based on your load up process, you can fetch the new service worker which will hopefully cache all your new content.

I have ran into weird issues with the service worker before, so this type of solution allows you to somewhat clear the old cache and refresh the state of your application without forcing users to clear their actual browser cache, while at the same time reloading your application.

EX (How to Unregister):

shell
serviceworker.unregister() .then( unregResult => { //You can check if successful with Promise result 'unregResult' window.location.reload(); })
2024年6月29日 12:07 回复

Store a version number in service‑worker.js

A one-character change in the controlling service-worker.js will automatically trigger a PWA update. Hence, if you store the version number in a const in this file and change it, an update of the PWA will be triggered.

How the update eventually is handled, is determined by the service-worker.js. Different update strategies may be defined in terms of the size and volatility of the PWA assets.

However, there is one important caveat. Make sure that the browser cache expiry directive set by the .htaccess file on your asset server is set to a really short duration (e.g. hours) or even to none. Failing to do so, will result in the browser not seeing any change for days to come in the manifest.json, nor the service-worker.js.

More details about service worker behaviour are available from Google Web Fundamentals.

2024年6月29日 12:07 回复

当您希望建立一个新版本的 Service Worker 并替换掉旧版本时,您需要确保新的 Service Worker 可以被安装并且激活,从而更新用户的页面。这个过程通常涉及以下步骤:

  1. 更新 Service Worker 文件: 为了触发 Service Worker 的更新检查,你需要对 Service Worker 的脚本文件做出一些变化。浏览器会比较 Service Worker 文件的字节码来决定是否需要安装一个新版本。

  2. 安装新的 Service Worker: 当浏览器检测到 Service Worker 文件有变化时,它会尝试安装新的 Service Worker。安装过程通常在 install 事件中完成。

  3. 等待新的 Service Worker 激活: 默认情况下,新的 Service Worker 在安装后不会立即激活,因为旧的 Service Worker 可能还在控制着当前页面。新的 Service Worker 会处于 waiting 状态,直到当前所有的页面都关闭,它才会被激活。

  4. 加速激活过程: 如果您想要强制新的 Service Worker 立即接管,您可以在新的 Service Worker 中使用 self.skipWaiting(),这样它就可以跳过等待直接进入激活状态。

  5. 更新客户端页面: 一旦新的 Service Worker 激活,旧的客户端(页面)需要被更新。可以在新的 Service Worker 的 activate 事件中使用 clients.claim() 来实现。这将允许新的 Service Worker 立即控制所有客户端。

这里是一个简单的例子,展示了如何在 Service Worker 脚本中使用 skipWaitingclients.claim

javascript
self.addEventListener('install', function(event) { // 执行安装步骤 event.waitUntil( // 缓存资源等操作 self.skipWaiting() // 强制进行 Service Worker 更新 ); }); self.addEventListener('activate', function(event) { event.waitUntil( clients.claim() // 让新的 Service Worker 接管页面 ); });

为了从页面脚本中强制刷新 Service Worker,你可以使用以下步骤:

  1. 注册 Service Worker(如果尚未注册)。
  2. 监听 Service Worker 的更新。
  3. 当检测到更新时,可以用 postMessage API 发送消息给 Service Worker,要求其强制更新。

例如:

javascript
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js').then(reg => { reg.addEventListener('updatefound', () => { const newWorker = reg.installing; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // 通知新的 Service Worker 接管控制权 newWorker.postMessage({ action: 'SKIP_WAITING' }); } }); }); }); } // Service Worker 文件中 self.addEventListener('message', (event) => { if (event.data.action === 'SKIP_WAITING') { self.skipWaiting(); } });

这样,您就可以强制刷新/更新 Service Worker 了。记住,虽然可以强制更新 Service Worker,但最好还是确保这样做对用户来说是平滑且不会导致数据丢失的。

2024年6月29日 12:07 回复

对于Service Worker的强制刷新或更新,以下是步骤和考虑事项:

  1. 更新 Service Worker 文件: 要使浏览器检查更新的 Service Worker,你需要更改 Service Worker 文件 (service-worker.js 或任何你命名的文件) 的内容。即使是一个简单的空格或注释的变化,都会导致浏览器认为 Service Worker 已经更新。

  2. Service Worker 生命周期: Service Worker 有一个内建的生命周期,用于管理安装和更新。当一个新的 Service Worker 被检测到,且与现有 Service Worker 不同时,它会进入 install 阶段,但不会立即控制页面,而是等待 activate 阶段。

  3. 跳过等待(Skip Waiting): 默认情况下,新的 Service Worker 会在等待状态,直到所有加载了旧 Service Worker 的页面关闭。为了让新的 Service Worker 立即接管,你可以在 Service Worker 文件中添加 self.skipWaiting(),这样新的 Service Worker 在安装结束后会立即进入激活状态。

    javascript
    // 在 Service Worker 的 'install' 事件中 self.addEventListener('install', event => { event.waitUntil( // 强制进行更新 self.skipWaiting() ); });
  4. 客户端的 Claim: 一旦激活,新的 Service Worker 应该开始接管控制页面。但是,如果你想让 Service Worker 立即控制所有客户端,包括加载旧 Service Worker 的页面,可以使用 self.clients.claim() 方法。

    javascript
    // 在 Service Worker 的 'activate' 事件中 self.addEventListener('activate', event => { event.waitUntil( self.clients.claim() ); });
  5. 更新客户端页面: 即使新的 Service Worker 激活并开始控制页面,你也可能需要刷新页面,以确保新的 Service Worker 接管并应用新的缓存策略或其他更新。你可以通过以下方式通知用户刷新页面,或者在某些情况下可以强制刷新。

    javascript
    // 通常,你可以提醒用户重新加载页面 if (navigator.serviceWorker) { navigator.serviceWorker.addEventListener('controllerchange', () => { // 在这里通知用户或刷新页面 console.log('Service Worker 已更新。'); window.location.reload(); }); }
  6. 更新缓存: 更新了 Service Worker 后,你还需要更新缓存的资源。你可以在 install 事件中通过删除旧缓存的方式来做到这一点。

    javascript
    self.addEventListener('install', (event) => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cache => { if (cache !== currentCacheName) { // 删除旧缓存 return caches.delete(cache); } }) ); }) ); });

通过使用以上技术,你可以确保 Service Worker 的更新能够快速且强制地推送到客户端,这对于紧急修复错误或推出新功能尤为重要。记住,Service Worker 的更新可能涉及对用户体验的中断,所以应该在用户不太可能被中断的时候进行更新,或者确保前端有相应的提醒和用户引导措施。

2024年6月29日 12:07 回复

你的答案