Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service worker offline support with pushstate and client side routing

I'm using a service worker to introduce offline functionality for my single page web app. It's pretty straightforward - use the network when available, or try and fetch from the cache if not:

service-worker.js:

self.addEventListener("fetch", event => {
    if(event.request.method !== "GET") {
        return;
    }
    event.respondWith(
        fetch(event.request)
            .then(networkResponse => {
                var responseClone = networkResponse.clone();
                if (networkResponse.status == 200) {
                    caches.open("mycache").then(cache => cache.put(event.request, responseClone));
                }
            return networkResponse;
        })
        .catch(_ => {
            return caches.match(event.request);
        })
    )
})

So it intercepts all GET requests and caches them for future use, including the initial page load.

Switching to "offline" in DevTools and refreshing at the root of the application works as expected.

However, my app uses HTML5 pushstate and a client side router. The user could navigate to a new route, then go offline, then hit refresh, and will get a "no internet" message, because the service worker was never told about this new URL.

I can't think of a way around it. As with most SPAs, my server is configured to serve the index.html for a number of catch-all URLs. I need some sort of similar behaviour for the service worker.

like image 330
user888734 Avatar asked Dec 24 '22 15:12

user888734


1 Answers

Inside your fetch handler, you need to check whether event.request.mode is set to 'navigate'. If so, it's a navigation, and instead of responding with a cached response that matches the specific URL, you can respond with a cached response for your index.html. (Or app-shell.html, or whatever URL you use for the generic HTML for your SPA.)

Your updated fetch handler would look roughly like:

self.addEventListener('fetch', event => {
  if (event.request.method !== 'GET') {
    return;
  }

  if (event.request.mode === 'navigate') {
    event.respondWith(caches.match('index.html'));
    return;
  }

  // The rest of your fetch handler logic goes here.
});

This is a common use case for service workers, and if you'd prefer to use a pre-packaged solution, the NavigationRoute class in the workbox-routing module can automate it for you.

like image 188
Jeff Posnick Avatar answered Dec 28 '22 11:12

Jeff Posnick