DEV Community

loading...

Sapper with "cache-first"

mweissen
・2 min read

The serviceworker of Sapper works with network first, falling back to cache if the user is offline. How to change the serviceworker to implement cache-first instead of network-first?

service-worker.js:

import { timestamp, files, shell, routes } from '@sapper/service-worker';

const ASSETS = `cache${timestamp}`;

// `shell` is an array of all the files generated by the bundler,
// `files` is an array of everything in the `static` directory
const to_cache = shell.concat(files);
const cached = new Set(to_cache);


self.addEventListener('install', event => {
    event.waitUntil(
        caches
            .open(ASSETS)
            .then(cache => cache.addAll(to_cache))
            .then(() => {
                self.skipWaiting();
            })
            .then(console.log("install ASSETS", ASSETS)) 
    );
});

self.addEventListener('activate', event => {
    event.waitUntil(
        caches.keys().then(async keys => {
            // delete old caches
            for (const key of keys) {
                if (key !== ASSETS) await caches.delete(key);
                }
            self.clients.claim();
        })
    );
});

self.addEventListener('fetch', event => {

    if (event.request.method !== 'GET' || event.request.headers.has('range')) return;

    const url = new URL(event.request.url);

    // don't try to handle e.g. data: URIs
    if (!url.protocol.startsWith('http')) return;

    // ignore dev server requests
    if (url.hostname === self.location.hostname && url.port !== self.location.port) return;

    // always serve static files and bundler-generated assets from cache
    if (url.host === self.location.host && cached.has(url.pathname)) {
        event.respondWith(caches.match(event.request));
        return;
    }

    // for pages, you might want to serve a shell `service-worker-index.html` file,
    // which Sapper has generated for you. It's not right for every
    // app, but if it's right for yours then uncomment this section
    /*
    if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) {
        event.respondWith(caches.match('/service-worker-index.html'));
        return;
    }
    */

    if (event.request.cache === 'only-if-cached') return;

    // for everything else, try the network first, falling back to
    // cache if the user is offline. (If the pages never change, you
    // might prefer a cache-first approach to a network-first one.)

    event.respondWith(
        caches
            .open(`offline${timestamp}`)
            .then(async cache => {
                try {
                    const response = await fetch(event.request);
                    cache.put(event.request, response.clone());
                    return response;
                } catch(err) {
                    const response = await cache.match(event.request);
                    if (response) return response;

                    throw err;
                }
            })
    );
});

Discussion (1)

Collapse
anirudhkonduru profile image
Anirudh Konduru

This seems to be identical to the service-worker.js that comes with the sapper starter project, without the change required to make it cache-first.
An short fix for anyone looking for the solution: replace the try block in the very last few lines with this:

let response = await cache.match(event.request);
if (response) return response;
response = await fetch(event.request);
cache.put(event.request, response.clone());
return response;