DEV Community

loading...

Sapper with "cache-first"

mweissen profile image 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)

pic
Editor guide
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;