1. Introduction:
What exactly a service worker? What is the need of service worker in our web application?
The service worker is the script that runs in the browser in the background. This means the service worker will run in a separate thread and is independent of our web pages. Service worker can't access the DOM directly.
The service worker is very handy for the caching strategy, push notification, background sync. This means we can manage which files need to be cache and which are not.
Also particularly for mobile app, when it is not opened and needs to send the push notification As well as if the app is not opened and needs to send the request to the server.
Please visit our previous articles on PWA:
2. Default Implementation of Service Worker in VueJs PWA:
VueJs application with Vue CLI uses the default configuration to set up and use the service worker.
If you build your application then, you can see the service worker file generated.
yarn build
There are two webpack plugin mode, one is GenerateSW mode which will generate the complete service worker. Vue CLI uses this as a default mode.
Another is InjectManifest mode, which allows us to customize and organize as our requirement and we do have full control over service worker.
You can see the default service worker generated after building the application as shown in the figure above.
We are going to use the InjectManifest mode so that we can have control over the service worker.
3. Register the Service Worker:
The service worker registration is done by Vue CLI by default. You can see the registerServiceWorker.js under /src folder, the file looks as below:
/* eslint-disable no-console */
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' +
'For more details, visit https://goo.gl/AFskqB'
)
},
registered () {
console.log('Service worker has been registered.')
},
cached () {
console.log('Content has been cached for offline use.')
},
updatefound () {
console.log('New content is downloading.')
},
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)
}
})
}
Note: make sure to change the service worker name if you use a different name for it.
Here, the service worker is enabled only for production, enabling service worker in development mode is not encouraged, as it will not reflect the current changes in development, instead it will use cached resources. All the events will be triggered in different cases.
4. Customize Service Worker with InjectManifest Mode:
Let's copy the service worker service-worker.js file from the dist/ folder or create a new service-worker.js file under the src/ directory.
Now, let's configure the config to notice that we are using the InjectManifiest mode and provide the newly created service worker path to use that service worker.
For this, create a vue.config.js file under the app root directory. Make sure to have the same name mentioned so this config file will be used by Vue CLI. The file looks as below.
Now, lets set up the config:
pwa:{
workboxPluginMode: "InjectManifest",
workboxOptions:{
swSrc:"./src/service-worker.js",
}
}
Add the above PWA configuration inside the
vue.config.js file. Here, we are using plugin mode as InjectManifest and give the path of the newly created service-worker.js file.
The custom service worker file needs to be as shown below:
workbox.core.setCacheNameDetails({prefix: "vue-pwa"});
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
Let's change the cache name from "vue-pwa" to "vue-pwa-test" and build the application. If you look into the service-worker.js under the dist/ folder and if you see the changes there, you are good to go.
Also, you can see some import scripts added at the beginning of the file by Vue CLI which looks something like below.
importScripts("/precache-manifest.7b241a43a2278739512d45eb32884cc1.js", "https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
The precache-manifest has a different json config for different files to cache. Note, the file name to be cache will be different in each build. Now, build and deploy the application.
Note: each change in the application will be reflected in the browser after new content is updated and the app is reloaded or refreshed.
Now, click on "skipWaiting" and reload the page which will reload our application with new changes. We will do this skipWaiting from the application in the next tutorial by giving the user control of the app update. Or if you do it right away especially for testing you can add the following inside service-worker.js.
workbox.core.skipWaiting();
You can see the caching files under the cache storage tab.
The life cycle of the service worker is implemented by default. You can handle each event/hooks inside registerServiceWorker.js file as mentioned in step 3.
Now, you can especially handle different push notifications, background sync, using indexdb inside this custom service worker created.
5. Service Worker Life Cycle Event:
If you want to implement the lifecycle event on your own you can use them inside the service-worker.js file itself. Make sure to unregister the service worker from the registerServiceWorker.js file and register your service worker. It can be done in the main.js file. Some of the life cycle events are below:
self.addEventListener('install', (event) => {
console.log("Installing ....................");
});
self.addEventListener('activate', (event) => {
console.log("activateing ....................");
});
self.addEventListener('fetch', (event) => {
console.log("fetching ....................");
});
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});