Search code examples
pythondjangoofflineservice-workerworkbox

service worker does my web slower! , how to add offline capabilities to Django work using workbox


I have been trying since some days to give offline capabilities using workbox to my Django Web App without success.

I have follow the get started guide and I manage to register with succeed the service worker and to save/serve from cache the the static and media resources.

Code done to archive the describe:

urls.py

...
url(r'^service-worker.js', cache_control(max_age=60*60*24)(TemplateView.as_view(
    template_name="sw.js",
    content_type='application/javascript',
)), name='sw.js'),
...

base.html template

...
<!-- bottom of body -->
<script>

  // Check that service workers are registered
        if ('serviceWorker' in navigator) {
          // Use the window load event to keep the page load performant
          window.addEventListener('load', () => {
            navigator.serviceWorker.register('{% url 'sw.js' %}');
          });
        }

</script>
...

sw.js (Service Worker)

importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.2.0/workbox-sw.js');

if (workbox) {
  console.log(`Yay! Workbox is loaded 🎉`);
} else {
  console.log(`Boo! Workbox didn't load 😬`);
}

workbox.setConfig({
  debug: false
});

// workbox.core.setLogLevel(workbox.core.LOG_LEVELS.debug);


workbox.routing.registerRoute(
  /\.(?:js|css)$/,
  workbox.strategies.staleWhileRevalidate({
    cacheName: 'static-resources',
  }),
);

workbox.routing.registerRoute(
  /\.(?:png|gif|jpg|jpeg|svg)$/,
  workbox.strategies.cacheFirst({
    cacheName: 'images',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
      }),
    ],
  }),
);

workbox.routing.registerRoute(
  new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
  workbox.strategies.cacheFirst({
    cacheName: 'googleapis',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 30,
      }),
    ],
  }),
);

After this , I decide to check the performance, and see if service workers were helping my app serving the cache files faster than before.

I leave here the 2 screenshots for you to check (Load times are in red on the right bottom):

516m without sevice worker, with default django cache enter image description here

1.09 s with service worker serving the cache files enter image description here

After this I have to say that I got shock, I was expecting an improvement and I got the opposite (I did something wrong on the previous steps?)

I did more test after, and in most of the other cases, the time of load is similar, but still I don't see big difference in favor of the service worker, specially on the first visits

But for the other part I was thinking , well 500ms~ if I get offline capabilities is a fair price to pay, but not even...

I add the following lines to the service worker to serve the pages when there was no net :

workbox.precaching.precacheAndRoute(
  [
    '/',
    '/offline',
  ],
  {
    directoryIndex: null,
  }
);

workbox.routing.registerRoute(
  /* my urls doesn't end in html, so i didn't found another way to 
  store only the html document except using the main route of my app as reg ex

example: http://localhost:8000/participation/id/title -> html for article
         http://localhost:8000/participation/ -> html for list of articles */

      new RegExp('participation/'), 
      workbox.strategies.networkFirst({
        cacheName: 'html-resources',
      })
    );

So now if i was offline in some of the pages of participation i was able to see them still, but this lead me to my actual problem.

Okey , so if a user tries to access now without net to a page it never visit before, I want to send him to my offline page where I just show him he is offline and he can go to X pages that he already visit

I didn't find any way to solve this problem, I try this:

workbox.routing.registerRoute(
  ({ event }) => event.request.mode === 'navigate', //if the requests is to go to a new url
  ({ url }) => fetch(url.href,{credentials: 'same-origin'}).catch(() => caches.match('/offline')) //in case of not match send my to the offline page
);

But it doesn't work at all, how I could do this?


Solution

  • I solved the problem by adding this to the service worker

    // Fallback to offline page if nothing is found in cache
    var networkFirstHandler = workbox.strategies.networkFirst({
      cacheName: 'default',
      plugins: [
        new workbox.expiration.Plugin({
          maxEntries: 10
        }),
        new workbox.cacheableResponse.Plugin({
          statuses: [200]
        })
      ]
    });
    
    const matcher = ({event}) => event.request.mode === 'navigate';
    const handler = (args) => networkFirstHandler.handle(args).then((response) => (!response) ? caches.match('/offline') : response);
    
    workbox.routing.registerRoute(matcher, handler);
    // End fallback offline