Devlog

Update cache with ServiceWorker

There are many examples of updating the cache of a service worker, but they all assume you want to create a new cache. Most of the time, you only want to update just 1 or 2 files, like a stylesheet and some javascript file. A simpler approach for this is to just use the same cache name, but change the serviceworker by adding a comment so it will be regarded as a new service worker.

For example if we want to change just the stylesheet style.css.

Our ServiceWorker looks like this:

// sw123
var CACHE_NAME = 'v1';
var CACHE_FILES = [
  'style.css',
  // and more
];

Now if we both change the style.css file and change the comment sw123 to sw124 then the service worker file will be downloaded and installed as a new serviceworker. In the install phase, the new style.css file will be installed in the “old” cache “v1” so will also be available to the active service worker as well. So at this point the new service worker is in the waiting phase but has already changed the cache in use by the active serviceworker.

At this point the new style.css will be used after the first reload. If you don’t want to wait for this, you can make the service worker to do 2 things:

  1. Use self.skipWaiting() in the install event – that way the service worker will be immediately go into the activate phase after installing.
  2. Use self.clients.claim() in the activate event – that way the controlled clients receive a controllerchange event

The client has to handle the controller change event and can then reload.

Relevant code service worker:

// sw123
var CACHE_NAME = "v1";
var CACHE_FILES = [
  "style.css",
  // and more
];

self.addEventListener("install", function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME).then(function(cache) {
      return cache.addAll(CACHE_FILES);
    }).then(function() {
      return self.skipWaiting();
    })
  );
});

self.addEventListener("activate", function activator (event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    }).then(function() {
      return self.clients.claim();
    })
  );
});

Relevant code client:

if ('serviceWorker' in navigator) {

  navigator.serviceWorker.register("service-worker.js")
    .then(function(registration) {
      console.log("OK!");
  });
 
  // Triggered by clients.claim() inside the
  // activate event of the service worker.
  navigator.serviceWorker.addEventListener("controllerchange",
    function(e) {
      var scriptURL = navigator.serviceWorker.controller.scriptURL;
      window.location.reload();
    }
  );
 
}