Open jan-tosovsky-cz opened 5 years ago
The
ServiceWorkerGlobalScope.skipWaiting()
method of theServiceWorkerGlobalScope
forces the waiting service worker to become the active service worker.Use this method with
Clients.claim()
to ensure that updates to the underlying service worker take effect immediately for both the current client and all other active clients.
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim
Does this not achieve what you want?
Edit: Also see https://developers.google.com/web/updates/2018/06/fresher-sw
Serviceworkers have a default max-age
of 24 hours. You can set UpdateViaCache: 'all'
(force the browser to use the HTTP cache for the serviceworker file) and then set Cache-Control: no-cache
for serviceworker.js
(force revalidation of the serviceworker file before serving it to the client).
I am aware of the ServiceWorkerGlobalScope.skipWaiting()
method, but I am discouraged by two points:
Source: https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68 (Approach 1)
Let's imagine a single page app loading some resource mapping initially (json in the html header) and via XHR requesting these resources (images) on demand. Version 2 requires different resources, so the mapping is different. If this v2 is opened in the new tab and waiting is skipped, the cache for v1 is deleted. That initially loaded mapping in v1 is still same (until refreshing the page), it still expects all resources in place, but they do not exist neither in cache nor on the server. The v1 gets broken unexpectedly.
In my 'immediate' approach I am trying to treat multiple SW versions separately per tab. Newer tab displays newer version while older one is still functional. In an ideal case the browser could display some alert on that old tab encouraging user to reload the page (already discussed in #1247).
While this approach covers rather a corner case (multiple tabs with the same app, but different versions), this could eliminate various workarounds, see aforementioned article.
The worst thing about blindly calling skipWaiting() is that it appears to work perfectly at first, but it results in bugs in production that are difficult to understand and reproduce.
Perhaps I'm missed something, but the article doesn't seem to be specific about the bugs. I think https://github.com/w3c/ServiceWorker/issues/863 would be a better solution to the problem of cache misses for the v1 tab. Mainly because the proposed "virtual" worker seems like a huge amount of added complexity for browser implementations.
I realized the described corner case when executing ServiceWorkerGlobalScope.skipWaiting()
could be eliminated if deleting the old cache is not triggered on activating the new Service Worker version. If Clients.claim() is not invoked, the old tab could serve old version files.
The currently used removing outdated caches on activating new SW version is taken from here: https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker#removing_outdated_caches
In my app every SW version is linked to new cache. So I could delete the exact cache for every SW version when this SW version is deactivated/unistalled.
However, currently there is no uninstall (or deactivate) event.
I might be miss something, but would it help to use push event when the worker needs to be update?
onpush = e => {
if (NeedsUpdate(e.data)) {
self.registration.update();
}
}
It'll just work as the service worker's lifecycle, so I don't think we need a hack around the cache. When unfortunately any of the tabs are opened when an update happens, we can ask to reload the page.
If offline-first app is deployed, then visited by users and later updated, these users do not see changes immediately on next visit. During the next visit SW is just updated. These changes are reflected after closing the tab, so effectively visible on future (if ever) visit.
In scenarios your new version is publicly announced and users are encouraged to visit the site again to enjoy new features, this offline-first approach (in the current form) is almost useless.
Such announcement should be complemented by very complicated instructions 'after visiting the page wait few seconds (to update cache), then close all tabs with the app (or the entire browser) and finally open the app again'.
Besides offline-first, aka 'Cache falling back to the network', there are other strategies: https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68
All seems to be overcomplicated for the above scenario. So I am suggesting another approach:
My proposal:
immediate
, which could be set totrue
(by default false).activate
event. It is serving requests only for the given tab.activate
event is fired.This would be appreciated by both users and frontend developers. The complexity is moved into the browser code.
Thanks for considering.