简体   繁体   中英

Service Worker skipWaiting unable to activate currently waiting SW

Description:

We are using sw precache for caching the scripts before hand, hence to update the scripts we are giving reload option, for that we are listening the worker message to skip waiting the newly install service worker for unknown reason we are not getting correct

importScript

// GETTING OLD SW reference (self) and NOT getting newly installed SW reference

self.addEventListener('message', function(event) {
    *// not working*
    self.skipWaiting();
});



// But if we put skipWaiting() in 'install' listener 
// it is getting correct new SW reference and working correctly

self.addEventListener('install', function(event) {
    // self.skipWaiting();
});

SW registration

if('serviceWorker' in window.navigator) {
      window.addEventListener('load', function() {
        window.navigator.serviceWorker.register("/serviceWorker.js").then(function(registration) {
          console.log("ServiceWorker registration successful with scope: ", registration);
          registration.onupdatefound = function() {
            console.log('NEW WILD WORKER HAS SPAWNED.!', registration);
            var installedWorker = registration.installing;
            installedWorker.onstatechange = function() {
              if (installedWorker.state === 'installed') {
                if (navigator.serviceWorker.controller) {
                  console.log('Updated content is available RELOAD!', navigator.serviceWorker.controller);
                  var el = document.getElementById('feature');
                  el.style['display'] = 'block';
                }
              }
            }
          }
        }).catch(function(error) {
          console.error("ServiceWorker registration failed: ", error);
        });
      });
      window.navigator.serviceWorker.addEventListener('controllerchange', function() {
        console.log('SERVICE WORKER UPDATED');
      });
    }

webpack config

new SWPrecacheWebpackPlugin({
        cacheId: 'pwa',
        filename: 'serviceWorker.js',
        staticFileGlobsIgnorePatterns: [/\.map$/, /\.json$/, /_nch\.[0-9a-z]+\.[js, css]+/g, /webpackManifest\.[0-9a-z]+\.js/g, /.DS_Store\.[0-9a-z]+/g],
        importScripts: ['offline/offline.1a2b3c4df1.js'],
        dontCacheBustUrlsMatching: /./,
        minify: false,
        skipWaiting: false,
        runtimeCaching: [ {
          urlPattern: /_nch\.[0-9a-z]+\.[js, css]+/g,
          handler: 'fastest',
          options: {
            cache: {
              name: 'jd-internal-script',
              maxEntries: 10,
            },
          },
        }, {
          urlPattern: /webpackManifest\.[0-9a-z]+\.js/g,
          handler: 'networkFirst',
          options: {
            cache: {
              name: 'jd-root-doc',
            },
          },
        }],
      }),

The best documentation for skipWaiting() can be found at https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/lifecycle#skip_the_waiting_phase

You can either call it unconditionally in the install handler, or follow the model that you seem to be doing, which is to listen for a message event and call skipWaiting() conditionally.

If you go the conditional route, then you should modify your client page's code to properly detect when the service worker you're registering enters the waiting state, and give the user the option of interacting with the page in a way that results in a corresponding postMessage() to tell the service worker to skipWaiting() . Based on what you're saying, you've tried this, but it looks like you're sending the message to the wrong service worker instance.

Here's what you page's code should look like:

// On your page:
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('service-worker.js').then(function(reg) {
      reg.onupdatefound = function() {
        var newSW = reg.installing;
        newSW.onstatechange = function() {
          if (newSW.state === 'waiting') {
            // This assumes there's a button with id='skip-waiting-button' that
            // users should click to get the new SW to activate immediately.
            var button = document.querySelector('#skip-waiting-button');
            button.addEventListener('click', function() {
              newSW.postMessage('skipWaiting');
            });
            // Assume that 'display' is 'none' initially.
            button.style.display = 'inline';
          }
          // Handle whatever other SW states you care about, like 'active'.
        };
      };
    })
  });
}

// In your service worker:
self.addEventListener('message', event => {
  if (event.data === 'skipWaiting') {
    self.skipWaiting();
  }
});

So here's what I think... Your new service worker is still waiting.. hence the event listener is still on your older service worker.. any messages you send will still be caught by the older sw(as the new one is waiting).

In case your only case is to get the new sw to act, you can simple refresh your page instead of sending the message

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