I have implemented Workbox to generate my service worker using webpack. This works - I can confirm revision is updated in the generated service worker when running "generate-sw": "workbox inject:manifest"
.
The problem is - I have noticed my clients are not updating the cache after a new release. Even days after updating the service worker my clients are still caching the old code and new code will only load after several refreshes and/or unregistering the service worker. For each release I have confirmed that the revision is updated.
I understand that I need to implement skipWaiting
to ensure the clients gets updated - especially PWA. I have read, and tried to follow the 3rd approach here: https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68.
My app mounts in app.js
I have added this code to
serviceWorker-base.js
addEventListener('message', function(messageEvent){
if (messageEvent.data === 'skipWaiting') return skipWaiting();
});
I have this code in app.js
const runServiceWorker = true
const serviceWorkerAvailable = ('serviceWorker' in navigator) ? true : false
// reload once when the new Service Worker starts activating
let refreshing
navigator.serviceWorker.addEventListener('controllerchange', function() {
if (refreshing) return
refreshing = true
window.location.reload()
}
)
function promptUserToRefresh(reg) {
// this is just an example - don't use window.confirm in real life; it's terrible
if (window.confirm("New version available! OK to refresh?")) {
reg.waiting.postMessage('skipWaiting')
}
}
function listenForWaitingServiceWorker(reg, callback) {
console.log('listenForWaitingServiceWorker')
function awaitStateChange() {
reg.installing.addEventListener('statechange', function() {
if (this.state === 'installed') callback(reg)
})
}
if (!reg) return
if (reg.waiting) return callback(reg)
if (reg.installing) awaitStateChange()
reg.addEventListener('updatefound', awaitStateChange)
}
// Register service worker
if (runServiceWorker && serviceWorkerAvailable) {
navigator.serviceWorker.register('/serviceWorker.js')
.then( (registration) => {
console.log('Service worker registered', registration)
listenForWaitingServiceWorker(registration, promptUserToRefresh) // <-- Added to existing code
})
}else{
console.log('Service worker disabled - process.env.NODE_ENV', process.env.NODE_ENV)
}
The problem with this code is that promptUserToRefresh()
only gets called on initial service worker install, not when a new service worker is waiting!
Also, I get the below error when accepting the first install.
TypeError: registration.waiting is null
promptUserToRefresh app.js:154
awaitStateChange app.js:162
The error gets triggered in promptUserToRefresh(registration)
by
registration.waiting.postMessage('skipWaiting')
I have also tested this approach with the same result: https://github.com/GoogleChrome/workbox/issues/1120