nlawson nlawson - 5 months ago 39
Javascript Question

How to refresh the page after a ServiceWorker update?

I've consulted a lot of resources on Service Workers:



However, I can't for the life of me figure out how to update the page after a new ServiceWorker has been installed. No matter what I do, my page is stuck on an old version, and only a hard refresh (Cmd-Shift-R) will fix it. No combination of 1) closing the tab, 2) closing Chrome, or 3)
location.reload(true)
will serve the new content.

I have a super simple example app mostly based on SVGOMG. On installation, I cache a bunch of resources using
cache.addAll()
, and I also do
skipWaiting()
if the current version's major version number doesn't match the active version's number (based on an IndexedDB lookup):



self.addEventListener('install', function install(event) {
event.waitUntil((async () => {
var activeVersionPromise = localForage.getItem('active-version');
var cache = await caches.open('cache-' + version);
await cache.addAll(staticContent);
var activeVersion = await activeVersionPromise;
if (!activeVersion ||
semver.parse(activeVersion).major === semver.parse(version).major) {
if (self.skipWaiting) { // wrapping in an if while Chrome 40 is still around
self.skipWaiting();
}
}
})());
});


I'm using a semver-inspired system where the major version number indicates that the new ServiceWorker can't be hot-swapped for the old one. This works on the ServiceWorker side - a bump from v1.0.0 to v1.0.1 causes the worker to be immediately installed on a refresh, whereas from v1.0.0 to v2.0.0, it waits for the tab to be closed and reopened before being installed.

Back in the main thread, I'm manually updating the ServiceWorker after registration – otherwise the page never even gets the memo that there's a new version of the ServiceWorker available (oddly I found very few mentions of this anywhere in the ServiceWorker literature):



navigator.serviceWorker.register('/sw-bundle.js', {
scope: './'
}).then(registration => {
if (typeof registration.update == 'function') {
registration.update();
}
});


However, the content that gets served to the main thread is always stuck on an old version of the page ("My version is 1.0.0"), regardless of whether I increment the version to 1.0.1 or 2.0.0.

I'm kind of stumped here. I was hoping to find an elegant semver-y solution to ServiceWorker versioning (hence my use of
require('./package.json').version
), but in my current implementation, the user is perpetually stuck on an old version of the page, unless they hard-refresh or manually clear out all their data. :/

Answer

Found the issue – you need to avoid any cache headers on the ServiceWorker JS file itself. Setting the cache to max-age=0 immediately solved the problem: https://github.com/nolanlawson/serviceworker-update-demo/pull/1

Cheers to Jake Archibald for setting me straight: https://twitter.com/jaffathecake/status/689214019308224513