Search code examples
browser-cacheindexeddbservice-workerprogressive-web-appsoffline-caching

Progressive web app using Service Worker: How to cache only static (non database dependent) parts of a page?


I have a web app (PHP/Laravel) that needs to work offline. The app is used to manage a number of people I'll call clients. My question concerns a page consisting of not much more than a table listing all our clients. Path is /clients.

Synchronization between MySQL (on server) and Indexed DB (for offline use) is working well, now I have to cache the static HTML of that page. My current approach caches the whole page including client data from the server. However, I don't want any data from MySQL in the cache, because when I'm offline, I get all the data from Indexed DB.

This is my simplified offline-worker.js:

var CACHE_NAME = 'app-cache';

var urlsToCache = [
  '/clients'
];

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/offline-worker.js');
}

self.addEventListener('install', function(evt) {
  evt.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
    })
  )
});

self.addEventListener('fetch', function(evt) {
  evt.respondWith(
    caches.match(evt.request)
      .then(function(response) {

        if (response)
          return response;

        var fetchRequest = evt.request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            if (!response || response.status != 200 || response.type != 'basic')
              return response;

            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(evt.request, responseToCache);
              });
            return response;
          }
        );
      })
  )
});

Right now, I'd remove the data as soon as the browser detects it's offline, but I'd rather cache a 'clean' version/template of that page from the beginning. This is a totally simplified version of /clients:

<h1>Our clients</h1>
<table>
  <tr>
    <td>Charles</td>
    <td>Chaplin</td>
  </tr>
  <!-- Imagine many more rows of server generated data here. 
       I'd like to get rid of all rows before caching. -->
</table>

What's the best way to cache /clients without any data from MySQL? I'm aware of the fact that the browser can't see what page elements come from a server database and which ones are static.

If I have to manually set up an offline version, that's fine. I just need to know how to tell the browser to use the offline version when there's no internet connection.


Solution

  • I would make a new url route /clientsTemplate where you return only the template. This template is stored in the app cache.

    var CACHE_NAME = 'app-cache';
    
    var urlsToCache = [
      '/clientsTemplate'
    ];
    
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/offline-worker.js');
    }
    
    self.addEventListener('install', function(evt) {
      evt.waitUntil(
        caches.open(CACHE_NAME)
          .then(function(cache) {
            return cache.addAll(urlsToCache);
        })
      )
    });
    

    Then you can implement the fetch as follow:

    self.addEventListener('fetch', function (event) {
      event.respondWith(
          fetch(event.request).then(function (response) {
            //the url concatenate is a bit hacky, works only if you don't have any url params
            return response || caches.match(event.request.url+"Template");
        })
      );
    });