DEV Community

Cover image for PWA for Vue applications: A practical guide
Pegah Safaie
Pegah Safaie

Posted on

PWA for Vue applications: A practical guide

Table Of Contents

What is PWA?

Consider the PWA as ice toppings that you can add to your Web App. Each topping is different but by blending them together they turn your Web App into one that tastes exactly like a native app. These flavors have always been available, but Google's chefs put them all together in one tray and called them Progressive Web Apps or PWA!
They even introduced the Lighthouse taster that lets us know how well our web app tastes like a PWA (or better yet, how well it tastes like a native App).
Many apps since then have added these toppings or parts of them to their websites. Some of the most famous ones are Spotify, Housing, and Starbox.

Do you need PWA tray?
In order to reply to this question, you need to know more about the toppings available in the PWA tray along with your customers preferences. If your customers don’t like strawberries in their ice cream, why would you pay for one?

  • 🍓 Reach offline experience: This means your app is available and fast even if the internet connection fails. If your web app is on the intranet, no one will care about this topping. However, people who live in developing countries and users who access your website on their mobile phones may very well value this topping.

  • 🍯 Installable: Installable apps are'nt just accessible via a URL in the browser, but also can be installed on android and ios devices.In this case users will click on an icon to open your app instead of writing the URL in the browser. That's it! You don't have to re-write your app in native or electron.

  • 🍒 Background sync: A Background sync feature lets you defer the actions if the user doesn't have a reliable connection. Imagine that your user fills out a form and presses 'Send'. But there is no connection currently. You don't want his efforts go to waste, so you gather the actions and send it to server as soon as the connection is back.

  • 🍇 push notification: What do you think about mobile app notifications, especially when they're closed? For instance: when you're watching TV and you're receiving a notification from the twitter app, saying: "Hey, somebody gave you a 👍. If you think it's a good idea to notify your users, then the Push notification feature might suit you.

How much time and effort should I spend on this tray?
When using PWA plugins, you can dramatically improve your PWA factor within 30 minutes. After that, it gets exponentially harder to make it better.

Fast decoration of your App using PWA Vue CLI plugin 🍓🍯

I hope you already know about Vue CLI plugins. But don't worry if you don't. They're pretty easy to understand.

Vue CLI is a npm package you install globally on your computer.
It lets you set up Vue projects quickly and without headaches.

Vue CLI Plugins are npm packages you add as dependency to your Vue CLI project,
They add some features and functionalities to your Vue project quickly and without headaches.

Here we use @vue/cli-plugin-pwa to add "🍓Reach offine experience" and "🍯Installable" support to our web app. That’s important to know: you don’t necessarily need a plugin to add PWA support, but using a plugin will make this process faster.

I use a simple Vue CLI project for this article. You can find it here but you can use any other Vue CLI project of your choice. If you don't know how to create a Vue CLI project look at this link.

Measure the PWA of your webpage before any changes
Before we make any changes let's see what does Google's taster says about us. I recommend you use Incognito, but Chrome or Firefox works too.
Before running Lighthouse tests on our web app, you have to deploy your app first:

  • Build your web app in production mode using:

     npm run build

    This command will generate a build directory named (by default) dist.

  • To run dist folder locally on a HTTP-server, Install the HTTP-server on your machine:

     npm install -g serve

    Now run the server with placing the build directly(dist) on the server:

     serve -s dist

During development you'll be able to use service worker through localhost, but to deploy it on a site you'll need to have HTTPS setup on your server.

  • Open the website in your localhost and go to devTools>Lighthouse. Select the "Progressive Web App" category and press 'Generate new report'. That's what you probably see after some minutes. PWA report before applying PWA plugin on my webpage

Apply PWA Plugin
If you are using Git, clean your local changes (commit, stash or discard them). This way you are able to see the changes that were added by the plugin.

To install and invoke PWA Vue CLI plugin execute this command:

 vue add pwa
Enter fullscreen mode Exit fullscreen mode

Measure Again
Build your app again in production mode and deploy it locally. Now we can ask Mr. Taster about the PWA factor.
PWA report before applying PWA plugin on your webpage
Congrats! It means that your app works offline and you can register it on "App store" or "Google play store". Take a Look at this article on how to get progressive web app into the google play store.

Get deeper

When I first applied PWA plugin to my app, I didn't have a good feeling about it. Even after receiving a Lighthouse taster award, My app was performing well without me knowing how. I wasn't even asked to config it during the installation process.

That's because the plugin does not aim to confuse you with PWA concepts but instead applies proven best practices that most web applications should follow. It assumes that if you know a lot about PWA configuration, you'll return to do your customizations.

With this section we go one level deeper to see how these best practices can be customized to meet your needs.

How does default caching affect my app?

As said before, "Reach Offline Experience" on a website means that it is fast and accessible even with an unreliable connection.
Service Workers are the heart and soul of this feature. A service worker is a script that runs independently from your webpage and acts as an interconnect between your website and network, implementing caching strategies to reduce traffic between the webpage and server.

This diagram below illustrates you how the service worker caches your essential static resources. The essential static resources are the resources that your webpage requires in order to be initiated. For example html, js, css and image files used in your home page are essential resources for your webpage.

Alt Text

This process is also logged to your console. Don't be confused with the word workbox. It is a webpack plugin used by your serviceworker to complete the precaching process. Let's learn more about it later on.
Consolelog shows us how precaching works

Looking at the application tab of your browser, you see all the precached resources.
PWA webpages cache the essential resources. We can see the list in application tab
Caching worked fine. But let's see how does caching affect the speed of your webpage?
When we refresh the page here is what happens:
How does caching avoid unnecessary network communication
I refreshed both the PWA and non PWA webpages in 3 different network modes. You can see the differences in network tab in the next pictures:

  • Refreshing without throttleing. The PWA webpage reads from the cache while the non PWA webpage downloads all the resources again. Still you don't see a big time difference in load time as the webpage is small and internet connection is pretty good.
    Refresh without throttling

  • By refreshing with slow 3G the load time difference is 2 times bigger.
    Refresh with slow 3G

  • Rereshing while offline does not load anything for non PWA webpage. The PW webpage on the other hand is loaded smoothly and fast.
    Refresh without connection

As you can see, with a PWA, there isn’t much difference between the three modes. That indicates this website is quite reliable even when the network is not stable.

Customize Vue CLI PWA plugin to support API caching

It's important to know what the PWA plugin does before we customize our application. Once you activate the plugin, it registers serviceworker.js in your main file. Registering serviceworker from the main file means we want serviceworker to get registered as soon as the page loads. Apart from that, PWA plugin does'nt do much more than organizing! Whenever the project is built, the PWA plugin tells Workbox to generate a serviceworker to cache the static resources. Workbox is the heart of caching strategies. Two modules in Workbox webpack plugins are responsible for implementing caching strategy: 'generateSW' and 'InjectManifest'.
We can use 'generateSW' when we don't have any configuration and we just want to precache essential static resources. Otherwise it's necessary to use InjectManifest. You can find more details on workbox webpack plugin here.

How does PWA plugin add precaching to your project

Improve Reach offline exprience of your users

If you prefer to skip the details and go directly to the code, visit my repository here on github.

We have seen how does Vue CLI PWA plugin applies precaching to our webpage. Now Let's improve the project by adding an API request to load some data. It gets an ip address and returns a country name.
By pressing "show country", an API request will be send to show you country name

  • How can I cache the response from my API? Can I rely on the default configuration of the PWA Vue CLI plugin?
    Unfortunately no! This time we have to add the caching strategy ourselves.

  • How should we start?
    First we need to determine our caching strategy. Google offers a list of caching strategies for a quick start called an offline cookbook.
    I also made a simple matrix out of these strategies, so it's easier for me to choose what I want.

    Updates Frequency / Fallback Support High Low
    True Network First/Stale While Revalidate Cache First
    False Network Only Cache Only

    Does my API response update often? No! We always get the same country for the same ip.
    Does my API need a fallback? Sure. I don't want to enter a risk and not show anything when my cache is empty for the first time.

    So i take Cache First strategy which says:

    We look in the cache first. If the resource is there we use it. If not, we get it from the network and also update the cache using the response of the network.

  • I know my strategy. Where should I put it?

    Vue CLI PWA plugin configuration is handled via the pwa property of either the vue.config.js file, or the "vue" field in package.json. You can find more details here

    In the previous section, we learned that the Workbox webpack plugin includes two caching modules: 'generateSW' and 'InjectManifest'. To customize our caching strategy, we call 'InjectManifest'. The InjectManifest Module requires a js file which contains our extra scripts or logic.

// Inside vue.config.js
module.exports = {
  // ...other vue-cli plugin options...
  pwa: {
    workboxPluginMode: 'InjectManifest',
    workboxOptions: {
      // swSrc is required in InjectManifest mode.
      swSrc: 'src/service-worker.js',
Enter fullscreen mode Exit fullscreen mode
  • Let's put our new strategy in the src/service-worker.js file.
// inside src/service-worker.js 

// define a prefix for your cache names. It is recommended to use your project name
workbox.core.setCacheNameDetails({prefix:  "simple-vue-project"});

// Start of Precaching##########################
// __precacheManifest is the list of resources you want to precache. This list will be generated and imported automatically by workbox during build time
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
// End of Precaching############################

// Start of CachFirst Strategy##################
// all the api request which matchs the following pattern will use CacheFirst strategy for caching
new  workbox.strategies.CacheFirst()
// End of CachFirst Strategy####################
Enter fullscreen mode Exit fullscreen mode

As you might have noticed, once you switch from 'GenerateSW' mode to 'InvokeManifest' mode, there is no default precaching and everything has to be added by hand to the script.

You might be wondering where the workbox came from. There was no import! Workbox injects the whole library from the official CDN during the build process. Not ideal but quick and easy for our tutorial. More information here.

Test our imporvements
Let's check the application and network tabs to make sure our code works. As we mentioned earlier: the app must be built and deployed to your localhost.

Alt Text

Alt Text
Once you make an API call, The response data will be cached and you can see it in your cache list. Now if you switch to offline mode and call the API again, you will receive the response data from the cache.

Congratulations! Your caching strategy has been customized! Please leave me a comment if you have further questions regarding the customization of your caching strategies😊.

Top comments (3)

dwrtardis profile image

I've found trying to get information on using PWA with the Vue CLI far harder than using PWA in a vanilla JS app, mainly due to the lack of documentation for newcomers. This tutorial is perhaps the best I have found on Vue CLI with PWA, so thank you very much for writing it.

The only part of the tutorial that initially confused me was where that precaching code in /src/service-worker.js came from, but if you look in the /dist/service-worker.js created by a generateSW build, you will find the precaching code comes from there.

Thanks again!

laurensiusadi profile image
Laurensius Adi

My Vue app uses websocket connection to transfer data, so I don't think caching is needed, since Vuex will handle all read and write while offline.
So simple setup should work right?
But I got this warning: "Site cannot be installed: Page does not work offline. Starting in Chrome 93, the installability criteria is changing, and this site will not be installable"

Any advice?

ratul16 profile image
Hasibul Alam Ratul

if the api request has an image url in it , how do you display that image when the app is offline ?