简体   繁体   中英

Where to put eventlisteners for serviceworker

Basicly I'm trying to accomplish to show a dialog when a new serviceworker version has been detected, and then to have user decide to reload to fetch it. To accomplish this we need to actively set skipWaiting before we reload window.

Here's my action:

  onClickHandler = () => {
    console.log('on click', 'posting skipWaiting');
    navigator.serviceWorker.controller.postMessage('skipWaiting');
  };

Here's my attempt to create the eventListener:

navigator.serviceWorker
    .register(swUrl)
    .then(registration => {
      registration.onupdatefound = () => {
        const installingWorker = registration.installing;
        if (installingWorker == null) {
          return;
        }
        installingWorker.onstatechange = () => {
          console.log('worker state', installingWorker.state);
          if (installingWorker.state === 'installed') {
            if (navigator.serviceWorker.controller) {
              console.log(
                'New content is available and will be used when all ' +
                  'tabs for this page are closed.'
              );

              navigator.serviceWorker.addEventListener('message', event => {
                console.log('skip waiting');
                if (event.data === 'skipWaiting') {
                  self.skipWaiting();
                }
              });
            }
          }
        };
      };
    })
    .catch(error => {
      console.error('Error during service worker registration:', error);
    });

The issue is that navigator.serviceWorker.addEventListener('message', event => does not get triggered. Am I declaring the listener wrong?

You are close. In your installed block you can make a check for

navigator.serviceWorker.controller

If this exists it means that the old content will have been purged and the fresh content will have been added to the cache. Its a perfect time to display a message or to force a refresh.

  navigator.serviceWorker.register('service-worker.js').then(function (registration) {

                    $log.debug('The service worker has been registered ', registration);

                    if(navigator.online) {
                        toastr.warning('Offline Mode', 'Application Status');
                    }

                    // updatefound is fired if service-worker.js changes.
                    registration.onupdatefound = function () {
                        // The updatefound event implies that reg.installing is set; see
                        // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
                        var installingWorker = registration.installing;

                        installingWorker.onstatechange = function () {
                            switch (installingWorker.state) {
                                case 'installed':
                                    if (navigator.serviceWorker.controller) {
                                        // At this point, the old content will have been purged and the fresh content will
                                        // have been added to the cache.
                                        // It's the perfect time to display a "New content is available; please refresh."
                                        // message in the page's interface.
                                        toastr.info('Application has been updated, please refresh this page for the most recent version');

                                        window.location.reload();

                                        });
                                        caches.delete('scope-dynamic').then(function () {
                                            $log.debug('Killed the dynamic cache!');
                                        })
                                        $log.debug('New or updated content is available.');
                                    } else {
                                        // At this point, everything has been precached.
                                        // It's the perfect time to display a "Content is cached for offline use." message.
                                        toastr.success('Content is cached for offline use.', 'Application Status');
                                        $log.debug('Content is now available offline!');
                                    }
                                    break;

                                case 'redundant':
                                    $log.error('The installing service worker became redundant.');
                                    break;

                            }
                        };
                    };
                }).catch(function (e) {
                    $log.error('Error during service worker registration:', e);
                });

There is some angular stuff sprinkled in there but that should help you get to where you wanna be.

The problem here is that the code you provided has only defined receiving & sending messages from your client to the service worker.

The below method has defined a message to be sent to your controller (active) service worker.

onClickHandler = () => {
    console.log('on click', 'posting skipWaiting');
    navigator.serviceWorker.controller.postMessage('skipWaiting');
};

The definition below has added an event listener on your ServiceWorker container to receive any messages sent from the service worker.

navigator.serviceWorker.addEventListener('message', event => {
    console.log('skip waiting');
    if (event.data === 'skipWaiting') {
        self.skipWaiting();
    }
});

You now need to define the event handlers from the service worker file to receive & send messages.

In the service worker file, to receive messages from the client:

self.addEventListener('message', function handler (event) {
   console.log('skip waiting');
   if (event.data === 'skipWaiting') {
      self.skipWaiting();
   }
});

To send messages from serviceworker to your client:

self.addEventListener('fetch', function(event) {
  self.clients.matchAll().then(all => all.map(client => client.postMessage('data from webworker')));
});

You could also send data back from the serviceworker using a MessageChannel. On your client you would have to define something like below:

    navigator.serviceWorker
        .register(swUrl)
        .then(registration => {
            var messageChannel = new MessageChannel();
            // listener for messages from the ServiceWorker    
            messageChannel.port1.addEventListener('message', (event) => console.log(event.data)); 

            function sendMessage (message) {
                //send message to the active service worker
                navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
            }
      onClickHandler = () => {
          sendMessage('skipWaiting');
      };
   });

I found the below article to be quite helpful.

ServiceWorker, MessageChannel & postMessage by Nicolás Bevacqua

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM