Building your first Progressive Web App (PWA) in Vue JS (The update | deploy problem & solution)

Building your first Progressive Web App (PWA) in Vue JS (The update | deploy problem & solution)

In this article, you'll see how simple and quick it is to create a PWA using VueJS. If you didn't create your very first Progressive Web App, this content might give you a great reason to do it. In addition, if you dislike PWA due to you had issues releasing new version and getting frustrated because the app wasn't updating, don't worry! The solution is easy-peasy

DISCLAIMER: My goal here is not to convince you to use PWA over native application or the best explanation about the technology itself. I rather prefer focus on how you can get it up running and give you awesome links and further readings.

Getting started

As soon you have the Vue CLI installed (npm install -g @vue/cli), basically what you need is to start a fresh project using the command below:


vue create myfirstpwa

Then make sure to select the PWA support

vue-init-v3.png

Note: It's not related to PWA, however, most the time I like to use the settings below, few free to change it to best switch your needs:

  • Vue Router
  • Linter
  • CSS Pre-processor

vue-init-v3-page2.png

Note: If you're interested in how to structure your vue projects, I'd like to recommend to you this post here

Then, you might have the project structure below:

myfirstpwa 
โ”œโ”€โ”€ babel.config.js
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ public
โ”‚   โ”œโ”€โ”€ favicon.ico
โ”‚   โ”œโ”€โ”€ img
โ”‚   โ”‚   โ””โ”€โ”€ icons
โ”‚   โ”œโ”€โ”€ index.html
โ”‚   โ””โ”€โ”€ robots.txt
โ””โ”€โ”€ src
    โ”œโ”€โ”€ App.vue
    โ”œโ”€โ”€ assets
    โ”‚   โ””โ”€โ”€ logo.png
    โ”œโ”€โ”€ components
    โ”‚   โ””โ”€โ”€ HelloWorld.vue
    โ”œโ”€โ”€ main.js
    โ”œโ”€โ”€ registerServiceWorker.js
    โ”œโ”€โ”€ router
    โ”‚   โ””โ”€โ”€ index.js
    โ””โ”€โ”€ views
        โ”œโ”€โ”€ About.vue
        โ””โ”€โ”€ Home.vue

If you deploy it, congratulations! you'll have your first PWA up running! ๐ŸŽ‰ See, how simple it's to get started with PWA using Vue JS?

If you tried to make your existing project a PWA, you might found that all you need is to:

  • Create a manifest.json
  • Create a link inside your index.html to the manifest.json

However, as you can notice, there is no manifest.json nor a link to it in the structure created by the vue create. That's why it will work if you deploy it. The npm run build will create the manifest inside the dist folder and everything will bet set for you.

I didn't wrote this article to stop here, I'd like to suggest few changes to make your PWA project a little bit better:

1) Explicit is better than implicit

As soon as the Zen of Python says, I rather prefer to create the manifest.json manually and be free to change it when I need to add some custom PWA settings (links down below). All you need to do is, create the file public/manifest.json and paste the content below:

{
  "name": "My PWA",
  "short_name": "My PWA",
  "start_url": "/index.html",
  "scope": ".",
  "display": "standalone",
  "orientation": "portrait",
  "background_color": "#CBFF4D",
  "theme_color": "#CBFF4D",
  "icons": [
    {
      "src": "./img/icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "./img/icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "img/icons/android-chrome-192x192.png",
      "sizes": "196x196",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "./img/icons/android-chrome-maskable-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "./img/icons/android-chrome-maskable-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "./img/icons/favicon-16x16.png",
      "type": "image/png",
      "sizes": "16x16"
    },
    {
      "src": "./img/icons/favicon-32x32.png",
      "type": "image/png",
      "sizes": "32x32"
    }
  ]
}

Then add the link to the manifest.json inside the public/index.html:

<!DOCTYPE html>
<html lang="">
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width,initial-scale=1.0" />
  <title><%= htmlWebpackPlugin.options.title %></title>
  <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
  <link rel="manifest" href="<%= BASE_URL %>manifest.json" />  <!-- HERE -->
  <meta name="theme-color" content="#cbff4d" />
</head>

Notes:

  • You might want to change the icons to your custom logo
  • You might want to change the Application name and the theme and background colors

2) Make the app updating work after releasing a new application version

The first big problem I faced within PWA was releasing a new version and the clients using it was in a very old version. Most of the time, even closing everything and starting again or forcing refresh (CTRL+F5) wasn't getting the latest version.

After a while and reading tons of articles (links down below), I came across the solution best worked for me:

Create in the project root, the vue.config.js and add the pwa content below:

const manifestJSON = require('./public/manifest.json')

process.env.VUE_APP_VERSION = require('./package.json').version

module.exports = {
  pwa: {
    themeColor: manifestJSON.theme_color,
    name: manifestJSON.short_name,
    msTileColor: manifestJSON.background_color,
    appleMobileWebAppCapable: 'yes',
    workboxOptions: {
      skipWaiting: true,
      clientsClaim: true,
    },
  },
}

Then, add the two small commands inside the src/registerServiceWorker.js:

  • a location.reload() inside the updatefound() method
  • a registration.waiting.postMessage({ type: 'SKIP_WAITING' }) inside the updated() method

Check the complete src/registerServiceWorker.js file 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, + refresh via href')
      location.reload()
    },
    updated(registration) {
      console.log('New content is available; refreshing.')
      registration.waiting.postMessage({ type: 'SKIP_WAITING' })
    },
    offline() {
      console.log(
        'No internet connection found. App is running in offline mode.'
      )
    },
    error(error) {
      console.error('Error during service worker registration:', error)
    },
  })
}

Lastly, add the code bellow inside the body of the public/index.html:

...
<body>
  <script>
    if (window.navigator && navigator.serviceWorker) {
      navigator.serviceWorker
        .getRegistrations()
        .then(function (registrations) {
          for (let registration of registrations) {
            registration.update()
          }
        })
    }
  </script>

 ...
</body>
...

After deploying yours PWA using those configurations and then releasing new versions, with a simple refresh (F5) or swipe down the page, the app will be updated.

NOTE: Off course, the user still need to refresh the page, however, most the time the app has a list or a home that the user keeps refreshing it to get the latest data from the backend. In this way, using the app, after few minutes they will get the latest version anyway. If this isn't your case, perhaps a small change is creating a dialog showing something like "Hey, there is a new version available, click here to refresh this page and get the new release"

If have any suggestion, or perhaps this post did help you, please, let me know in the comments down below.

I'd like to see/understand how people are using PWA nowadays, I seriously keep seeing apps that are native apps (heavy apps by the way) where a PWA would be much pleasant for the final users, anyway, this is a subject for other post...

Links:

ย