Turn your Vue Web App into a PWA!

How to convert your Vue Web App into a Progressive Web Application. Tutorial with examples.

Rajat S
Bits and Pieces

--

In this post, we will see how to take an existing Vue App that I had created in my previous post, and convert it into a PWA!

Progressive Web Apps (PWA) are web apps that can run smoothly on mobile devices. But that is not all! PWAs are not restricted by any kind of restrictions and rules placed by the Play Store or App Store.

This is a great plus point for those of us who have experienced the troubles of following the App store’s rules on when we can update the update the app, or what that update can contain; The App Store won’t let you publish the update if it changes the fundamental working of the app, so you can’t update your calculator app to start playing music instead of crunching numbers.

Another great thing about PWAs is that they can load under uncertain network conditions, even offline, which is awesome. Let’s get started.

Tip: Use open-source tools like Bit to easily isolate, share and reuse components across your projects and apps. It saves time and makes it easier for your team to collaborate, share components and build faster.

Learn more:

Base App

The base app that we will be working on is a simple Vue App that uses Firebase for Authentication and Database. I had previously written a post on how to build this app. You can read it here:

But you can also get the source code of the app here:

Use the git clone command to download the code into your system:

$ git clone https://github.com/rajatk16/dc-covers

Or if you for some reason have never used GitHub, simply click on the download as zip button.

Then open the command terminal and the install command using NPM or Yarn.

npm install // or yarn install

Once the installation of packages is done, you will notice a new folder named .node_modules is created in the app’s folder. To check if the app is working, run npm run dev or yarn dev to launch the app on the browser:

With that, we can get started with turning this basic web app into a progressive one!

Manifest

In order to create a Progressive Web App, the browser must first know that it is a PWA. This is down by creating a Web App Manifest. A Web App Manifest is a JSON file that defines some information about the application. It is very similar to how an Android’s manifest file looks like and contains info such as name, author, and more.

Inside the root directory of the project, create a new file named manifest.json.

Once again, this file is used to describe the app with the help of some meta data as shown below:

{
"name": "DC-Covers",
"short_name": "Covers",
"start_url": "index.html",
"display": "standalone",
"theme_color": "#0476F2",
"background_color": "#fff",
"icons": [
{
"src": "", // usually kept in the static/icons folder
"sizes": "", // enter the size of the image
"type": "" // enter the type of the image eg: image/png,
},
]
}

Here we have written the name as DC-Covers and a short_name as Covers. If your apps name is already short, you can skip adding this property. You can’t give a shorter name to an app named something like Whatsapp! 😆

Next we have a property named start_url, this refers to the main html file of our app, which in this case is the index.html file. We will then use the display property to tell our browser how to open the app. standalone value will open the app like a normal one. You can set this property to something like fullscreen or minimal-ui.

The theme_color will be used to set the color of any toolbar on our mobile devices, and the background_color will be used by the PWA’s splash screen.

icons property is an array of object and can be used to define the icons of our app.

One last thing left to do! Go to the index.html file and add a link tag that points to this manifest.json file.

<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.css" />
<link rel="manifest" href="/manifest.json"/>
<title>DC Comics Rebirth - Covers</title>
</head>
<body>
<div id="app"></div>
<script src="/build/build.js"></script>
</body>
</html>

But how do we know if this has worked?

If we go open the DevTools in our browser, you will see a tab named Application. In there, you should get something like this

Meta Tags

Apart from the Web App Manifest file that we created in the previous section, we can also add a couple of meta tags to provide better information. This will help a few other browsers like iOS and Microsoft Edge know that the app here is progressive and hence installable.

Inside the index.html file, write the following meta tags inside the head section.

<meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="DC-Covers"><meta name="msapplication-TileImage" content="link to the image in static folder"><meta name="msapplication-TileColor" content="#000">

The first three tags for the iOS browser/device. The first tag tells iOS that the web app is installable. Next tag configures the style of the status bar and I have set to the user’s default status bar. The last iOS tag is where we can tell iOS what the title of our app is.

Next, we define meta tags for the Microsoft Edge browsers, first of while directs to smaller icons stored inside the static/icons folder. The TileColor tag defines the tile color that Windows uses to place the icon on the home screen.

We also need to add a link to the icon that we want to display on the home screen of an iOS home screen as shown below:

<link rel="apple-touch-icon" href="link to the smaller icon">

With that, our app is recognized by all the modern browsers.

Caching with Service Workers

Service workers are a big part of what defines a Progressive Web App. They are scripts that run in the background and make all the difference between web page and a web app. Service workers are used to implement features that do not really need any user interaction and provide the basis on which features like offline operation, periodic background syncs, and push notifications rely on.

Creating service workers is not an easy task. And since each feature requires a separate service worker, the task can also get repetitive. We will instead use sw-precache to automate the process of service workers creation.

Let’s start by globally installing sw-precache using NPM:

$ npm install sw-precache -g

Once installed, create a configuration file called sw-config.js and write the following code inside it:

module.exports = {
staticFileGlobs: [
'index.html',
'manifest.json',
'dist/**.js'
]
}

This file is basically telling that we need to cache index.html, manifest.json and all the JavaScript files stored in the dist folder.

Then run sw-precache command with --config flag linked to the sw-config.js file as shown below:

$ sw-precache --config sw-config.js

This will create a new file named service-worker.js that contains a ton of code that defines a service worker that caches all the file mentioned above.

Automate Server Worker Generation

In the previous section we manually generated a server worker. But what we really need to do is automate it inside the build process. This can be done using the sw-precache-webpack-plugin as a developer dependency.

First, install the this package as a devDependency:

$ npm install -D sw-precache-webpack-plugin

Then, open the webpack.config.js and import the above devDependency as shown below:

const SWPrecache = require('sw-precache-webpack-plugin')

At the bottom, you will notice an if statement that defines all the plugins used in the app. Write a new instance of SWPrecache inside it as shown below:

new SWPrecache({
cacheId: 'dc-covers',
filepath: 'service-worker.js',
staticFileGlobs: [
'index.html',
'manifest.json',
'dist/*.{js,css}
],
stripPrefix: '/'
})

Run the npm run build command, and you will see that a new file named service-worker.js is created.

The next step is to register this service worker into the browser.

Service Worker Registration

As mentioned before, once the service worker is created, we need to register it into the browser. The simplest way to do this is by write the registration code inside the src/main.js file.

Inside the main.js file, we will use the navigator API. Navigator has a property named serviceWorker with a register method that takes the URL of the service-worker.js file.

We also need to make sure that the browser supports service workers. If the browser does not support service workers, there is no point in running the registration code.

Also, service workers are only generated when the app is in production mode. So we also need to make sure that the code is only run when the app is running in production mode.

const prod = process.env.NODE_ENV === 'production'
const shouldSW = 'serviceWorker' in navigator && prod
if (shouldSW) {
navigator.serviceWorker.register('/service-worker.js').then(() => {
console.log("Service Worker Registered!")
})
}

Rebuild the app using npm run build and then launch it using http-server or the Live Server extension of VS Code. In the Application section of the browser’s devtools, you will notice that the Service Workers sections is not so empty anymore.

After registering the service worker in the browser, our app will have the static files cached. But we are still working in development mode. So caching will not allow us to see any new changes in our app.

The browser updates the service worker by doing a byte to byte comparison of the modified version and the old version. If they are any different, the modified version will replace the old version.

But if we make any changes, rebuild the app, and run the app on the browser, you will notice that nothing has changed. This is because the service worker has complete control over the cache for some time (usually for 1 hour).

Unregister the Service Worker Automatically

Just as we automated the service worker registration process, we can do the same with the unregistration process as well. This is helpful for when you are still developing the app, and don’t want the service worker to cache anything.

First we need to create a new service worker for development mode. I will call this file service-worker-dev.js. The whole point of this service worker is to overwrite the original service worker of the production mode.

Add an eventListener on the install event that will call a function that calls skipWaiting. Doing this will force activate the service worker.

self.addEventListener('install', () => {
self.skipWaiting()
})

I will also add another EventListener for the activate event. This will help us access all other tabs and windows of the browser that will use this service worker.

self.addEventListener('activate', () => {
self.clients.matchAll({
type: 'window'
}).then(clients => {
for (let client of clients) {
client.navigate(client.url)
}
})
})

Similar to last section, we need to use this service worker by adding it to the main.js file.

const shouldSWDev = 'serviceWorker' in navigator && !prod

Then, we add an else if to the if statement from previous sections that checks if the mode is not production.

else if (shouldSWDev) {
navigator.serviceWorker.register('/service-workder-dev.js').then(() => {
console.log('Service Worker Registered!')
})
}

Now run the app using npm run dev. Then you will notice in the DevTool’s application section that the service worker file has changed!

Conclusion

In this post, we first briefly saw what progressive web apps are. Then we created a “manifest” to help browsers recognize our web pages as web apps. We also added a few meta tags to help other browsers recognize the web page as an app. This way, the app potentially becomes installable.

We then create our first service worker, manually. Then we saw to how to automate the process inside the webpack server. Finally, we registered the service worker, saw why changes do not get update on the browser, and then created a new service worker that un-registers our original service worker in development mode.

There are a lot more things that we can do to make our app progressive. Chrome’s DevTools can “audit” our app and tell us how Progressive the app is, and what more we can do to improve it.

Also, while creating our app’s project directory using Vue-CLI, we can simply use the vue-init pwa vue-app.

This will make it easier for us to create the our Progressive Web App!

Thanks for reading, and please feel free to comment and ask anything 😄 Cheers

--

--