Search code examples
vue.jsvuejs2service-workerprogressive-web-appsvue-cli-3

Inform App.vue a Service-Worker Event is Being Called


I have a project created using Vue CLI 3 with Vue's PWA plugin included. I would like to display a banner prompting the user to click an in-app “Refresh” link as described here in the 'Approach #3' section.

But in my Vue.js app, because the service-worker code is executed in main.js, and my snackbar banner is built into my App.vue component, I'm not sure how to trigger my showRefreshUI() method once the service-worker updated() event has been called.

main.js (applicable portion)

import Vue from 'vue';
import App from './App';
import './registerServiceWorker';

new Vue({
  router,
  render: h => h(App),
}).$mount('#app');

register-service-worker (applicable portion)

import { register } from 'register-service-worker';

if (process.env.NODE_ENV === 'production') {
  register(`${process.env.BASE_URL}service-worker.js`, {
    updated (registration) {
      console.log('New content is available; please refresh.');
      // I'd like to call App.vue's showRefreshUI() method from here.
    },
  });
}

App.vue (applicable portion)

<script>
export default {
  name: 'App',
  mounted () {
    // Alternatively, I'd like to call this.showRefreshUI() from here
    // when the service worker's updated() method is called.
  },

  methods: {
    showRefreshUI () {
      // My code to show the refresh UI banner/snackbar goes here.
    },
  },
};
</script>

If I can't call the showRefreshUI() method from main.js, how might I pass something from the updated() event to App.vue's mounted() lifecycle hook to accomplish the same basic thing?


Solution

  • The final working solution for me was to leave main.js untouched, and instead:

    register-service-worker (applicable portion)

    import { register } from 'register-service-worker';
    
    const UpdatedEvent = new CustomEvent('swUpdated', { detail: null });
    
    if (process.env.NODE_ENV === 'production') {
      register(`${process.env.BASE_URL}service-worker.js`, {
        updated (registration) {
          console.log('New content is available; please refresh.');
          UpdatedEvent.detail = registration;
          document.dispatchEvent(UpdatedEvent);
        },
      });
    }
    

    App.vue (applicable portion)

    <script>
    export default {
      name: 'App',
      data () {
        return {
          registration: null,
        };
      },
      mounted () {
        document.addEventListener('swUpdated', this.showRefreshUI);
      },
      beforeDestroy () {
        document.removeEventListener('swUpdated', this.showRefreshUI);
      },
      methods: {
        showRefreshUI (e) {
          this.registration = e.detail;
          // My code to show the refresh UI banner/snackbar goes here.
        },
      },
    };
    </script>