Jeff E Mandel
Jeff E Mandel

Reputation: 81

Reload only changed files in serviceworker cache

I am using a ServiceWorker to precache files. The serviceworker.js is generated whenever I build the application, and all files newer than the last version of serviceworker.js are added to the file as an array, as well as the current timestamp, so if I modify a cached file it is added to the cache and the timestamp changes, so the serviceworker is reinstalled. In the install event handler, I delete the new files then add them:

newFiles.forEach((resource) => {
    cache.delete(resource)
        .then(() => { })
        .catch((error) => { console.warn(`${resource}: ${error}`); });

});
newFiles.forEach((resource) => {
    cache.add(resource)
        .then(() => { })
        .catch((error) => { console.warn(`${resource}: ${error}`); });
}

I claim the clients:

self.addEventListener('activate', (event) => {
event.waitUntil(clients.claim())});

I can see the correct timestamp for the file in my cache: Image of Chrome application cache

In the serviceworker, I log cache hits:

/css/main.css cached

Unfortunately, the page renders with the previous version of main.css. If I do a hard reload, I get the page rendered with the new css, but if I then do a regular reload, the old css comes back. Requesting the file with curl gets the new version. So it seems the cached file is retained. I know that I can delete the entire cache, and I know I can change the name of the file to main-datestamp.css, but this seems inelegant. What am I missing?

Upvotes: 0

Views: 20

Answers (1)

Jeff E Mandel
Jeff E Mandel

Reputation: 81

The problem was that I have 2 versions of the program; demo and production; these run at different URLs. I cache more files in demo, so I had demo-cache for https://demo.example.com and production-cache for https://production.example.com. So when I updated the css file in demo-cache, it still found it in production-cache because I was reading from all caches:

const responseFromCache = await caches.match(request);

I changed this to:

const responseFromCache = await caches.match(request,{'cacheName': 'demo-cache'});

Also, the cache.delete is unnecessary. So how do we handle someone deleting the cache? Since we have a complete list of resources, we just use Set operations:

cache.keys().then((keys) => {
    const resourceSet = new Set(myresources);
    const cacheSet = new Set(keys);
    const difference = resourceSet.difference(cacheSet);
    const newfilesSet = new Set(newFiles).union(difference);
    newfilesSet.forEach((resource) => {
        cache.add(resource)
            .then(() => { })
            .catch((error) => { console.warn(`${resource}: ${error}`); });
    })
})

Note that the intent of this code is to cache a limited number of files, in my case images and audio files that are required for animations.

Upvotes: 0

Related Questions