Vue Cli 3 how to use the official PWA plugin ( Service Worker )
As already pointed out, it's more of a "service workers" issue than a "vue cli" one. First of all, to make sure we're on the same page, here's what the boilerplate content of registerServiceWorker.js should look like (vue cli 3, official pwa plugin):
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready () {
console.log(
'App is being served from cache by a service worker.\n'
)
},
cached () {
console.log('Content has been cached for offline use.')
},
updated () {
console.log('New content is available; please refresh.')
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
If you haven't changed the BASE_URL variable in your .env file, then it should correspond to the root of your vue app. You have to create a file named service-worker.js in the public folder (so that it's copied into your output directory on build).
Now, it is important to understand that all the code in the registerServiceWorker.js file does is register a service worker and provide a few hooks into its lifecycle. Those are typically used for debugging purposes and not to actually program the service worker. You can understand it by noticing that the registerServiceWorker.js file will be bundled into the app.js file and run in the main thread.
The vue-cli 3 official PWA plugin is based on Google's workbox, so to use the service worker, you'll have to first create a file named vue.config.js at the root of your project and copy the following code in it:
// vue.config.js
module.exports = {
// ...other vue-cli plugin options...
pwa: {
// configure the workbox plugin
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// swSrc is required in InjectManifest mode.
swSrc: 'public/service-worker.js',
// ...other Workbox options...
}
}
}
If you already have created a vue.config.js file, then you just have to add the pwa attribute to the config object. Those settings will allow you to create your custom service worker located at public/service-worker.js
and have workbox inject some code in it: the precache manifest. It's a .js file where a list of references to your compiled static assets is stored in a variable typically named self.__precacheManifest
. You have to build your app in production mode in order to make sure that this is the case.
As it is generated automatically by workbox when you build in production mode, the precache manifest is very important for caching your Vue app shell because static assets are usually broken down into chunks at compile time and it would be very tedious for you to reference those chunks in the service worker each time you (re)build the app.
To precache the static assets, you can put this code at the beginning of your service-worker.js
file (you can also use a try/catch statement):
if (workbox) {
console.log(`Workbox is loaded`);
workbox.precaching.precacheAndRoute(self.__precacheManifest);
}
else {
console.log(`Workbox didn't load`);
}
You can then continue programming your service worker normally in the same file, either by using the basic service worker API or by using workbox's API. Of course, don't hesitate to combine the two methods.
I hope it helps !
as an addition to the answer above: I wrote a small guide on how to go further and add some functionality to the custom service-worker, using the setup above. You can find it here.
Four main things to keep in mind:
- configure Workbox in
vue.config.js
toInjectManifest
mode, pointing theswSrc
key to a custom service-worker file in/src
In this custom service-worker, some lines will be added automatically in the Build process for importing the
precache-manifest
andworkbox CDN
. Following lines need to be added in the customservice-worker.js
file to actually precache the manifest files:self.__precacheManifest = [].concat(self.__precacheManifest || []); workbox.precaching.suppressWarnings(); workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
Listen to registration events in the
registerServiceWorker.js
file. You can use the registration object that is passed as first argument to the event handlers to post messages to theservice-worker.js
file:... updated(registration) { console.log("New content is available; please refresh."); let worker = registration.waiting worker.postMessage({action: 'skipWaiting'}) }, ...
Subscribe to messages in the
service-worker.js
file and act accordingly:self.addEventListener("message", (e)=>{ if (e.data.action=='skipWaiting') self.skipWaiting() })
Hope this helps someone.