PWA (Progressive Web Apps) uses modern Web apis and traditional Progressive enhancement strategies to create cross-platform Web applications. These apps are ubiquitous and feature-rich, giving them the same user experience advantages as native apps.

The way users usually use PWA apps is by adding H5 pages to the phone home screen (desktop). What does “Add to the home screen” mean?

Add to Home Screen (A2HS) is a feature in modern smartphone browsers that makes it easy for developers to Add shortcuts to their favorite Web applications (or websites) to the Home Screen so that users can access it later with a single click.

Mobile Chrome/Android Webview supports A2HS from version 31, Opera for Android from version 32, and Firefox for Android from version 58.

How can our application support A2HS?

  • Applications serve over HTTPS – the Web is moving towards greater security and many modern Web technologies, including A2HS, will only work in secure environments.
  • Link the manifest file with the correct fields from the HTML header.
  • Appropriate ICONS can be displayed on the home screen.
  • Chrome also requires the app to register a Service Worker (so it can run offline).

Please refer to PWA Docs for detailed instructions

Following the above approach, let’s explain how to enable our NUXT application to support A2HS:

  • First, our application should be deployed on HTTPS services. (Most sites are HTTPS.)
  • Create the manifest file in NUxt:

Because we’re going to add it to app.html

<link rel="manifest" href="/manifest.webmanifest">
Copy the code

According to the NUxT access path, we should add the manifest static file to the nuxT static folder, so we create the manifest.webmanifest file in the static folder, which defines the configuration of our PWA desktop application.

{"background_color": "# FFFFFF ", // The background color for installing or launching the app "theme_color": "# FFFFFF ", // the UI color for using the "description": "Test pWA ", // Application description "display": "standalone", // Desktop application shell h5 display mode, full screen, independent, minimal UI or browser" ICONS ": [{" SRC ": "icon/36-min.png", "sizes": "36x36", "type": "image/png" }, { "src": "icon/48-min.png", "sizes": "48x48", "type": "image/png" },{ "src": "icon/72-min.png", "sizes": "72x72", "type": "image/png" },{ "src": "icon/96-min.png", "sizes": "96x96", "type": "image/png" },{ "src": "icon/144-min.png", "sizes": "144x144", "type": "image/png" },{ "src": "icon/192-min.png", "sizes": "192x192", "type": "image/png" },{ "src": "icon/256-min.png", "sizes": "256x256", "type": "image/png" },{ "src": "icon/512-min.png", "sizes": "512x512", "type": "Name ": "test", // the full name of the website app "short_name": "Test ", // the short name displayed on the desktop" start_URL ": "/? Open "related_applications": [{"platform": "play", "id": "XXX ", "url": [{"platform": "play", "id":" XXX ", "url": "XXX"}] // Associate Google app}Copy the code
  • Then register a Serviceworker in the Nuxt application

We can simply create sw.js in the static folder, where we can define two event listeners

self.addEventListener('install', (e) => {e.waituntil (caches.open('test-store').then((cache) => cache.addall ([// Can add file list you want to cache '/ XXXX/XXXX ']))}) self.addEventListener('fetch', (e) => { e.respondWith( caches.match(e.request).then((response) => response || fetch(e.request)) ) })Copy the code

Now that we have sw.js, we need to register it at window.onload

window.addEventListener('load',function(){ if ('serviceWorker' in window.navigator) { window.navigator.serviceWorker .register('sw.js') .then(() => { console.log('Service Worker Registered'); }) .catch(() => { console.log('Service Worker Registered Failed'); }); }})Copy the code

So when we open our H5 in an A2HS-enabled browser, such as Chrome on an Android phone, click on the menu in the upper right corner to see the option to install the PWA app on the phone desktop.

If you want to cache packaged JS and static image files, you should import the cached file path into the ServiceWorker at build time. For this, Google provides a plugin called workbox-webpack-plugin. We configure it in the nuxt.config.js plugin:

build: {
    extend(config, { isDev, isClient }) {
        const worboxWebpackPlugin = require("workbox-webpack-plugin")
        if (isClient) {
            config.plugins.push(
                new worboxWebpackPlugin.GenerateSW({
                  cleanupOutdatedCaches: true,
                  clientsClaim: true,
                  skipWaiting: true,
                  maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
                  swDest: path.join(__dirname, '/src/static/sw.js'),
                  manifestTransforms: [
                    async (manifestEntries) => {
                      const manifest = manifestEntries.filter(entry => {
                        return entry.url.indexOf('../server') === -1;
                      });
                      return {manifest, warnings: []};
                    }
                  ]
                })
            )
        }
    }
}

Copy the code

Since the NUxT build is the file that contains the server, we need to strip them out and actually cache the client-side files. SwDest is the generated path for redefining sw.js, and is placed in the static folder just like sw.js in the previous demo.

Plug-in specific configuration parameters and function can be reference: developers.google.com/web/tools/w…

After nuxt builds, sw.js and workbox-{hash}.js will be generated in the static folder.

A simple application of PWA in NUXT is implemented. In fact, we want the user to click on a button or prompt on the H5 page to trigger the installation of the PWA app, rather than click on the install app in the browser menu. How does this work? This requires us to listen for and use the browser’s beforeInstallPrompt event.

In NuxT, we implement a prompt or install button by writing a vUE component. In NuxT, we implement a prompt or install button by writing a VUE component.

window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); // Prevent Chrome 67 and earlier versions from automatically displaying the installation prompt this.deferredPrompt = e; // Save the event, click to evoke the installation needs to use this.show(); // Display the component when the event is triggered})Copy the code

Then bind a click event to the component:

handleClick() { if (this.deferredPrompt) { this.deferredPrompt.prompt(); this.deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } else { console.log('User dismissed the A2HS prompt'); } this.deferredPrompt = null; }); } this.hide(); // Hide the component after clicking}Copy the code

After completion, we find that there is a high probability that the component is not displayed, which means that the beforeInstallPrompt event we are listening for did not fire successfully. Why is this? This is because the beforeInstallPrompt event is triggered earlier than component Mounts, so if the browser has already triggered the beforeInstallPrompt event and we listen in component Mounts, the listener will not be triggered. So we need to improve.

Beforeinstallprompt normally fires after window.onload. We can add a listener to window.onload in the script in app.html:

window.addEventListener('beforeinstallprompt',(e) => {
  console.log(e);
  e.preventDefault();
  window.deferredPrompt = e;
});
Copy the code

/ / Add the event to the global variable. / / add the event to the global variable.

// If beforeInstallPrompt is raised before component Mounted after window.onload, If (window.deferredPrompt) {this.deferredPrompt = window.deferredPrompt; this.show(); } else {// If beforeInstallPrompt is not raised when component mounted after window.onload, Then rebuild the listener window.addEventListener(' beforeInstallPrompt ', (e) => {console.log('bar', e); e.preventDefault(); this.deferredPrompt = e; this.show(); }); }Copy the code

And you’re done!