VueJS PWA: Cache Busting

Gerard Lamusse
Vue.js Developers
Published in
3 min readMay 14, 2019

Building a PWA has become a lot easier with the help of plugins and libraries such as @vue/cli-plugin-pwa however, though they set up a service-worker and manifest file they don’t handle updates so well.

The reason for this is that cache isn’t properly cleared once a new site is built and installed, and from what I can tell it all has to do with the fact that the service-worker doesn’t tell the difference between different versions of your PWA.

Version it

Assuming you have used the aforementioned @vue/cli-plugin-pwa plugin, we need to update three files to get cache busting to work.

Service-worker.js

The first file is the actual service-worker, located usually at the root of your pwa eg. /service-worker.js. We’ll update it as follows:

// service-worker.js

workbox.core.setCacheNameDetails({ prefix: 'd4' })
//Change this value every time before you build
const LATEST_VERSION = 'v1.5'
self.addEventListener('activate', (event) => {
console.log(`%c ${LATEST_VERSION} `, 'background: #ddd; color: #0000ff')
if (caches) {
caches.keys().then((arr) => {
arr.forEach((key) => {
if (key.indexOf('d4-precache') < -1) {
caches.delete(key).then(() => console.log(`%c Cleared ${key}`, 'background: #333; color: #ff0000'))
} else {
caches.open(key).then((cache) => {
cache.match('version').then((res) => {
if (!res) {
cache.put('version', new Response(LATEST_VERSION, { status: 200, statusText: LATEST_VERSION }))
} else if (res.statusText !== LATEST_VERSION) {
caches.delete(key).then(() => console.log(`%c Cleared Cache ${LATEST_VERSION}`, 'background: #333; color: #ff0000'))
} else console.log(`%c Great you have the latest version ${LATEST_VERSION}`, 'background: #333; color: #00ff00')
})
})
}
})
})
}
})

workbox.skipWaiting()
workbox.clientsClaim()

self.__precacheManifest = [].concat(self.__precacheManifest || [])
workbox.precaching.suppressWarnings()
workbox.precaching.precacheAndRoute(self.__precacheManifest, {})

What this essentially does is versioning every service-worker you create. Next time a user visits your site it checks the version numbers to see if they match and if not clears all the cache.

It is important to note that this will only work if you remember to update the value of LATEST_VERSION every time you build and deploy a new pwa version.

registerServiceWorker.js

The second file is a small change to src/registerServiceWorker.js. All we want to do is reload the page once the service-worker has been updated.

import { register } from 'register-service-worker'

if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready () {
console.log('Site is ready')
},
cached () {
console.log('Content has been cached for offline use.')
},
updatefound () {
console.log('New content is downloading.')
},
updated () {
console.log('New content is available; Refresh...')
setTimeout(() => {
window.location.reload(true)
}, 1000)
},

offline () {
console.log('No internet connection found. App is running in offline mode.')
},
error (error) {
console.error('Error during service worker registration:', error)
},
})
}

I’ve highlighted the important part. The reason for the timeout is to just give enough time for the service-worker to activate otherwise you get stuck in a reload loop since it calls updated and then tries to activate but then reloads.

vue.config.js

Last but not least we have to update the pwa plugin options to use our service-worker instead of generating a new one.

const manifestJSON = require('./public/manifest.json')module.exports = {
pwa: {
themeColor: manifestJSON.theme_color,
name: manifestJSON.short_name,
msTileColor: manifestJSON.background_color,
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'black',
workboxPluginMode: 'InjectManifest',
workboxOptions: {
swSrc: 'service-worker.js',
},

},

The part in bold is the important required part.

Once you’ve made these changes you can build your app remembering to change the version every time you do a new build.

If you open the console after pushing a new version, you’ll see the app clearing and updating before automatically reloading the page.

NOTE: It is important to note that if you already have a live app, then this won’t work unless the user does a full refresh which 99% is not going to happen. So your best option would be to add a small script to your index.html that uninstalls all service-workers for a week/month.

if(window.navigator && navigator.serviceWorker) {
navigator.serviceWorker.getRegistrations()
.then(function(registrations) {
for(let registration of registrations) {
registration.unregister();
}
});
}

--

--