Building a Vue PWA with Web Push Notifications enabled

Web Push Notifications & Service Workers

Web Push Notifications

Web Push Notifications are one of the most important feature for a PWA in order to be compared with native apps. User will get notification even if your app is not opened in the browser. If you want to learn more about them or you don’t know much about the Web Push Notifications you can find more info here

Service Workers

In order to work, Web Push Notifications need a service worker, basically a javascript process that runs indipendently from your app. To learn more about service workers I suggest you to read the documentation in order to understand better what I will present below. More info and some good example here

PWA Service Worker in Vue

Default service workers

Using Vue CLI with the PWA template you will get a service worker automatically created, registered and working (production env). The standard service worker is created by the build process by registerServiceWorker.js :

/* 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)
    }
  })
}

src/registerServiceWorker.js

The result after the build will be service-worker.js in the root of the dist folder

/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/

importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");

importScripts(
"precache-manifest.c9d50e7b61e4d64b7ee75abc3a4955c0.js"
);

workbox.core.setCacheNameDetails({prefix: "myappname"});

/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

The auto-generated service worker is a standard template built loading a Workbox CDN and it’s a basic caching system of the js files.

So what about I want setup my service worker to enable WebPush notifications?

The only way to get them working is to create a custom service worker.

Custom service worker

The best solution to me in order to create a custom service worker that works with the Web Push Notifications was to change the auto-generated service worker.

How to do that? The process is quite simple:

  1. change or create a vue.config.js in the root of your app (not the src folder) adding the following
module.exports = {
  pwa: {
    workboxPluginMode: "InjectManifest",
    workboxOptions: {
      swSrc: "src/service-worker.js",
    }
  }
}

vue.config.js

This tell to the Vue build process to inject the service worker source from src/service-worker.js that you have to create.

  1. create an empty js file src/service-worker.js and add
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

//Web Push Notifications//
let click_open_url
self.addEventListener('push', function(event) {
  let push_message = event.data.json()
  // push notification can send event.data.json() as well
  click_open_url = push_message.notification.data.url
  const options = {
    body: push_message.notification.body,
    icon: push_message.notification.icon,
    image: push_message.notification.image,
    tag: 'alert'
  };
  event.waitUntil(self.registration.showNotification(push_message.notification.title, options));
});

self.addEventListener('notificationclick', function(event) {
  const clickedNotification = event.notification;
  clickedNotification.close();
  if ( click_open_url ){
    const promiseChain = clients.openWindow(click_open_url);
    event.waitUntil(promiseChain);
  }
});

Basically we keep the standard auto-generated service-worker adding our push notification event listener. In this way you enable your PWA to get Web Push Notifications.

How to test it? In order to test you need to build your app, and a Web Push Notifications service (Firebase, Google Cloud Messaging, ecc.) or you can create your own.

Important: push notifications need subscription by the user.

Create a VAPID Web Push Notification Service

Click here to learn how to create a VAPID Web Push Notification service

See you soon!