How to load different files from cache?

Viewed 100

I am using service worker to provide a fallback page that shows the user is offline. The service worker during interception of request, fetches the same request and on error on fetching, provides response for 'offline.html' request from the cache. A small snippet of doing this is.

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then(() => {
      return fetch(event.request).catch((err) => {
        return caches.match("offline.html");
      });
    })
  );
});

now if the offline html has other request, probably to its css files, or images, how do I load them from cache. I've tried doing the following:

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then(() => {
      return fetch(event.request).catch((err) => {
        let url = event.request.url;
        if(url.endsWith('.css')) return caches.match('offline.css');
        if(url.endsWith('.jpg') || url.endsWith('.png')) return caches.match('images/banner.jpg');
        return caches.match("offline.html");
      });
    })
  );
});

But is there a better way of doing this? Is there a standard way of doing this?

1 Answers

First off, I would recommend checking to see whether event.request.destination === 'document' before you decide whether or not to use offline.html as the fallback content. That ensure that you're not accidentally returning an HTML document to satisfy, say, a random API request that happens to fail.

Additionally, your current code includes caches.match(event.request) but then doesn't actually used the cached response, which is likely not what you intend.

That said, let's walk through what I think is your desired logic:

  • Your service worker attempts to make a request against the network.
  • If that request returns a valid response, use it, and you'd done.
  • If that request fails, then:
    • If it was a navigation request, regardless of the destination URL, use the cached offline.html for the response.
    • Otherwise, for non-navigation requests (like CSS or JS requests), use the cached entry matching the desired URL for the response.

Here's a service worker that implements that. You'll need to ensure that the CSS, JS, and offline.html assets are cached during service worker installation; this just includes the fetch handler logic.

self.addEventListener('install', (event) => {
  event.waitUntil(
    /* Cache your offline.html and the CSS and JS it uses here. */
  );
});

async function fetchLogic(request) {
  try {
    // If the network request succeeds, just use
    // that as the response.
    return await fetch(request); 
  } catch(error) {
    // Otherwise, implement fallback logic.
    if (request.mode === 'navigate') {
      // Use the cached fallback.html for failed navigations.
      return await caches.match('offline.html');
    }
    // Otherwise, return a cached copy of the actual
    // subresource that was requested.
    // If there's a cache miss for that given URL, you'll
    // end up with a NetworkError, just like you would if
    // there were no service worker involvement.
    return await caches.match(request.url);
  }
}

self.addEventListener('fetch', (event) => {
  event.respondWith(fetchLogic(event.request));
});

There's also some formal guidance in this article.

Related