Search code examples
vue.jsnuxt.jsprogressive-web-apps

Nuxt/pwa update version


I'm creating a pwa using the nuxt/pwa module. I managed to detect a change in the service worker on the install event of the workbox:

plugins/pwa-update.js

export default async (context) => {
  const workbox = await window.$workbox
  if (!workbox) {
    alert("Workbox couldn't be loaded.")
    return
  }

  workbox.addEventListener('installed', (event) => {
    if (!event.isUpdate) {
      alert('The PWA is on the latest version.')
      return
    }
    
    console.log(workbox)

    alert('There is an update for the PWA, reloading...')
    
    window.location.reload()
  })
}

and clear the old cache version with:

#static/custom-sw.js

self.addEventListener('activate', (event) => {
    const LATEST_VERSION = 'v0.998'
    if (caches) {
        caches.keys().then((arr) => {
        arr.forEach((key) => {
            if (key.indexOf('app-precache') < -1) {
            caches
                .delete(key)
                .then(() =>
                console.log(
                    `%c Cleared ${key}`,
                    'background: #333; color: #ff0000'
                )
                )
            } else {
            caches.open(key).then((cache) => {
                cache.match('version').then((res) => {
                if (!res) {
                    cache.put(
                    'version',
                    new Response(LATEST_VERSION, {
                        status: 200,
                        statusText: LATEST_VERSION,
                    })
                    )
                } else if (res.statusText !== LATEST_VERSION) {
                    caches
                    .delete(key)
                    .then(() =>
                        console.log(
                        `%c Cleared Cache ${res.statusText}`,
                        'background: #333; color: #ff0000'
                        )
                    )
                } else {
                    console.log(
                    `%c Great you have the latest version ${LATEST_VERSION}`,
                    'background: #333; color: #00ff00'
                    )
                }
                })
            })
            }
        })
        })
    }
})

Here is my nuxt.config.js

export default {
  // Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
  ssr: false,
  ...,
  plugins: [
    ...,
    { src: '~/plugins/pwa-update.js', ssr: false },
  ],
  pwa: {
    meta: {
      title: 'App',
      description: 'App',
      author: '...',
    },
    icon: {
      source: 'icon2.png',
      fileName: 'icon2.png',
      iconSrc: 'icon2.png',
    },
    manifest: {
      name: 'App',
      short_name: 'App',
      lang: 'it',
      theme_color: '#0ca86c',
      background_color: '#0ca86c',
      useWebmanifestExtension: false,
      display: 'standalone',
      start_url: '/',
    },
    workbox: {
      enabled: true,
      cacheNames: {
        prefix: 'app',
      },
      importScripts: [
        'custom-sw.js',
      ],
    },
  },
}

BUT, I dont really understand how the update detection works. For example, if I deploy a new server side version with the updated version in the custom-sw.js file, I still need to manually reload the page to detect the change and show the new version alert. I was not expecting that the user has to manually reload the page. Is it possible to trigger the pwa-update event listener without reloading the page?
For example, with a push notification?


Solution

  • Here's how I implemented a procedure to check for new updates. Please keep in mind that I haven't tested it with a installable app, just with the website version

    1. Create a static file version.txt that contains a random string. That file is regenerated upon build. This means, any new npm run generate will change the content of that file
    2. I have a component in all layouts that is in charge of fetching the version.txt file every few minutes. When it is fetched, I compare the contents with some item that I keep in localStorage, say _app_version. If the item is not present in localStorage, it means the app was loaded for the first time. If it exists and it's different than the new one it means the server has new code and I force the user to reload the app using a blocking/unclosable confirmation modal.