javascriptservice-workerservice-worker-events

How to delete service worker caches using urls


I have this service worker file (sw.js) which I got from the internet:

const PRECACHE = 'precache-v1.1';
const RUNTIME = 'runtime';

// A list of local resources we always want to be cached.
const PRECACHE_URLS = [

  /* index page */
  'index.html', './',

  /* stylesheets */
  './assets/css/bootstrap.min.css', './assets/css/style.css', 

  /* javascripts */
  './assets/js/scripts.js', 

  /* images */
  './assets/images/logo.png'

];

// The install handler takes care of precaching the resources we always need.
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(PRECACHE)
      .then(cache => cache.addAll(PRECACHE_URLS))
      .then(self.skipWaiting())
  );
});

// The activate handler takes care of cleaning up old caches.
self.addEventListener('activate', event => {
  const currentCaches = [PRECACHE, RUNTIME];
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return cacheNames.filter(cacheName => !currentCaches.includes(cacheName));
    }).then(cachesToDelete => {
      return Promise.all(cachesToDelete.map(cacheToDelete => {
        return caches.delete(cacheToDelete);
      }));
    }).then(() => self.clients.claim())
  );
});

// The fetch handler serves responses for same-origin resources from a cache.
// If no response is found, it populates the runtime cache with the response
// from the network before returning it to the page.
self.addEventListener('fetch', event => {
  // Skip cross-origin requests, like those for Google Analytics.
  if (event.request.url.startsWith(self.location.origin)) {
    event.respondWith(
      caches.match(event.request).then(cachedResponse => {
        if (cachedResponse) {
          return cachedResponse;
        }

        return caches.open(RUNTIME).then(cache => {
          return fetch(event.request).then(response => {
            // Put a copy of the response in the runtime cache.
            return cache.put(event.request, response.clone()).then(() => {
              return response;
            });
          });
        });
      })
    );
  }
});

On every page of my website, say index.html, about.html, contact.html, I have this code:

if('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js'); }

The reason why I have this code on every page is because I have a lot of pages on my website and that I want the browser to cache all the files if the user lands on any page of my website.

Now for example, when a user visits about.html, the service worker caches all the files listed in sw.js and also caches the current page even though it's not listed in sw.js. This is exactly what I want since I have hundreds of pages on my website and I don't want to manually list all of them in the sw.js file. The problem is, when I update the version number in the sw.js, the browser deletes all the old caches which are listed in sw.js and re-caches the files again, except about.html. Whenever the user visits this page again, the old about.html gets displayed and all the other files are new.

How can I overcome this issue? I want to delete absolutely all the files cached by my website and not just the files listed in sw.js and since about.html is not listed, this cached page doesn't get updated.


Solution

  • The problem is that your about.html file is saved in the cache named runtime which is not versioned. So this cache is still the same, even you deploy a new sw.js with an incremented PRECACHE-cache. (e.g precache-v1.2)

    The solution is to add versioning to the runtime-cache as well:

    const PRECACHE = 'precache-v1.1';
    const RUNTIME = 'runtime-v1.1';
    

    Or cleaning the runtime cache on activate:

    self.addEventListener('activate', event => {
    // only PRECACHE (which is already the new version, setup on install event) should not be deleted.
    const currentCaches = [PRECACHE];
    ...