Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add message event listener when skipWaiting: false #3704

Closed
loliconer opened this issue Mar 25, 2019 · 24 comments
Closed

Add message event listener when skipWaiting: false #3704

loliconer opened this issue Mar 25, 2019 · 24 comments

Comments

@loliconer
Copy link

What problem does this feature solve?

When developing PWA, the default workboxPluginMode is generateSW, and they set skipWaiting: false. In this case, the generated service worker should allow our web app to trigger skipWaiting() on demand, by using postMessage() to communicate with our service worker.

This feature was implemented in Workbox v4.1.0, but Vue CLI 3.x can't take advantage of it.

What does the proposed API look like?

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

Reference: skipWaiting option in workbox-webpack-plugin

@LinusBorg
Copy link
Member

LinusBorg commented Mar 25, 2019

The script you posted is registered in the service worker by Workbox.

You can send a message to the Service worker like this:

navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING'});

a good place for that would be in the updated hook inside the register-service-worker.js file that you will find in your project's /src folder.

@loliconer
Copy link
Author

@LinusBorg
Yes, I can send a message to the SW, my code in registerServiceWorker.js:

    updated (reg) {
      console.log('New content is available; please refresh.')
      window.addEventListener('refreshSW', function () {
        reg.waiting.postMessage({ type: 'SKIP_WAITING' })
      })
    },

The problem is the ServiceWorker can't skip waiting after posting message, because it hasn't a listener to listen the message event. So the code:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

should be included in the service-worker.js, which is auto-generated. But now the code doesn't exist in service-worker.js.

@LinusBorg
Copy link
Member

Ah, yeah that's because we are still using workbox 3.

Workbox 4 is on our roadmap though (see #3649), and with that update, this will just work.

@loliconer
Copy link
Author

The roadmap is a great job, thanks for your contributions. Looking forward to v4.
During Vue CLI 3.x, my workaround is auto appending the code to the generated service-worker.js after every build, which seems to be a little troublesome.

@LinusBorg
Copy link
Member

well, you can always use the injectSW startegy instead of the default generateSW and add that code to your serviceworker file.

@loliconer
Copy link
Author

Thanks. It works well.

@wizardpisces
Copy link

wizardpisces commented Apr 3, 2019

well, you can always use the injectSW startegy instead of the default generateSW and add that code to your serviceworker file.

injectSW mode would bring other problems in service-worker.js, eg:

importScripts( "/precache-manifest.d9dc0105d8b5bafb421964b66d031695.js" );

this hash code is auto generated by webpack which makes precache-manifest injection another headache

@wizardpisces
Copy link

Thanks. It works well.

Hi, how did you inject that code? I kind of also haunted by this problem. Thanks

@loliconer
Copy link
Author

@wizardpisces
The importScripts statement will still be auto injected into the final service-worker.js. So just config the vue.config.js like this:

pwa: {
  workboxPluginMode: 'InjectManifest',
  workboxOptions: {
    swSrc: 'src/service-worker.js'
  }
}

@wizardpisces
Copy link

Then how to inject generateSW Mode related configuration like runtimeCaching offlineGoogleAnalytics navigateFallback directoryIndex , did you mannually added into service-worker.js?

@loliconer
Copy link
Author

Sorry, I did not use these configurations. You may have a try.

@wizardpisces
Copy link

Sorry, I did not use these configurations. You may have a try.

thanks

@vesper8
Copy link

vesper8 commented May 17, 2019

am also haunted by this problem.. @wizardpisces would you mind sharing your code/strategy on how you are injecting the listener into the generated service-worker.js after every build?

@vesper8
Copy link

vesper8 commented May 17, 2019

also, out of curiosity, instead of going through all this trouble, why not just set skipwaiting:true in vue.config ? If your plan is to trigger skipWaiting every time the code is updated, doesn't this achieve the same?

@loliconer
Copy link
Author

@vesper8 Set skipwaiting: true in vue.config.js will directly update the frontend resources to the latest version without notify the visitors. But sometimes we want the visitors notice that there is an update and then they can update the app manually.

@vesper8
Copy link

vesper8 commented May 19, 2019

@loliconer I actually tried to set skipWaiting: true in vue.config.js but I couldn't figure out how to do it. Would you mind sharing how that can be set so it updates right away as you said? I'd appreciate it! Thanks

@loliconer
Copy link
Author

@vesper8

pwa: {
  workboxOptions: {
    skipWaiting: true
  }
}

@wizardpisces
Copy link

am also haunted by this problem.. @wizardpisces would you mind sharing your code/strategy on how you are injecting the listener into the generated service-worker.js after every build?

Which mode did you use ?
I use GenerateSW mode, importScripts option saved my life , you could easily inject code into service-worker.js after evert build.
If you choose InjectManifest mode, the problem would not exist at all

more detailed infomation you could find here

@vesper8
Copy link

vesper8 commented Jun 8, 2019

@LinusBorg @loliconer @wizardpisces

I am now using the v4 alpha which comes with Workbox 4

I have tried adding this navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING'}); inside the updated() hook inside register-service-worker.js

But this gives me the following error:

Error during service worker registration: TypeError: Cannot read property 'postMessage' of null

I have also tried adding

updated(reg) {
    window.addEventListener('refreshSW', function () {
      reg.waiting.postMessage({ type: 'SKIP_WAITING' })
    })
}
`
``

but this does nothing

I would really really like to know how to immediately clear the cache when new content is detected. I am so boggled that this is so damn hard to accomplish and how is this not affecting thousands of vue-cli developers? Everytime I push a new breaking change it seems like all my users run into issues with the cache not being cleared

@wizardpisces
Copy link

@LinusBorg @loliconer @wizardpisces

I am now using the v4 alpha which comes with Workbox 4

I have tried adding this navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING'}); inside the updated() hook inside register-service-worker.js

But this gives me the following error:

Error during service worker registration: TypeError: Cannot read property 'postMessage' of null

I have also tried adding

updated(reg) {
    window.addEventListener('refreshSW', function () {
      reg.waiting.postMessage({ type: 'SKIP_WAITING' })
    })
}
`
``

but this does nothing

I would really really like to know how to immediately clear the cache when new content is detected. I am so boggled that this is so damn hard to accomplish and how is this not affecting thousands of vue-cli developers? Everytime I push a new breaking change it seems like all my users run into issues with the cache not being cleared

this article might help you

@vesper8
Copy link

vesper8 commented Jun 9, 2019

thank you! I actually applied this very solution by following this tutorial yesterday https://www.blog.plint-sites.nl/adding-workbox-to-a-vue-cli-pwa/

except that I am not exactly giving users a choice, I force them to update. That's because my app is still under heavy development and too often I roll out breaking changes to the API that pretty much require people to use the new front-end or else things are broken, so allowing users to skip the update just results in the site being broken.

It seems I might have done something wrong however because the prompt to update only occurs after a user does a page refresh.. not when they are continuing to use the app and a new version has been pushed. Is that how it's supposed to work or is it supposed to detect that a new version is available while a user is using the current app?

@wizardpisces
Copy link

You could interval detect new version then update or give user a choice, also you could wait until refresh or second time opening app then detect update, it's very flexible.
It all depends on how important or urgent it is to update the app

@Dean-Christian-Armada
Copy link

Dean-Christian-Armada commented Apr 26, 2020

The script you posted is registered in the service worker by Workbox.

You can send a message to the Service worker like this:

navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING'});

a good place for that would be in the updated hook inside the register-service-worker.js file that you will find in your project's /src folder.

This did not work accordingly. I was expecting that visiting my site will automatically include the new updates. Or an existing tab with my website opened with just a simple refresh (not hard refresh)

I am using workbox "@vue/cli-plugin-pwa": "^4.2.0",. Basically the code snippet below in service-worker.js does not seem to listen:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

@leolux
Copy link

leolux commented May 1, 2020

The script you posted is registered in the service worker by Workbox.

You can send a message to the Service worker like this:

navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING'});

a good place for that would be in the updated hook inside the register-service-worker.js file that you will find in your project's /src folder.

I can see that the postMessage gets received by the service worker. The strange thing is that the event received in the service worker does not contain the data property. Instead the event only contains this: {"isTrusted":true}

@LinusBorg Any idea why the event does not contain the postMessage: {"data":{"type":"SKIP_WAITING"}} ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants