How do I create a simple service worker to add new

2019-05-24 16:30发布

问题:

I am wanting to take the response from a simple API, use it to create a series of URLs (an array), that I then want to feed to a service worker, and have it add each entry to the offline storage.

For background, I have some limited experience with sw-precache (sw-precache-webpack-plugin), sw-toolbox, and JavaScript in general, though that is changing. Currently have a webpack + vue setup getting data from a simple API I built a long time ago.

A user who visits my site likely wants to have one or more types of content available offline - which might require anywhere from 30 requests (30 individual URLs) to add to cache, up to as many as 4000+. The upper limit would be expected to increase over time. Running Cache.addAll() the way that I've attempted before makes the client crash - too many requests all at once. I think importScripts option may be the thing I need, but am not sure where to start. How do I write a script that would act as a supplement to the service worker which I could send an array to?

To summarize:

  • Using webpack / vue
  • Basic familiarity with sw-precache and sw-toolkit
  • Need tens to thousands of pages possibly cached in one user request
  • Would like to know how to go about developing a solution to the problem, such as snippets of example code for an importScripts, or another webpack plugin that might do the trick, or other alternative methods which incorporate Service Workers.

In Development Code: https://laoadventist.info

Simple API for getting URLs to cahce: https://laoadventist.info/api/r2

An individual example API result that I would like to cache (one button click requires tens to thousands of requests): .../api/d

UPDATE:

I was able to come up with a somewhat functional solution... It's not perfect, or in a service worker, but it does work. Suggestions for improvement are welcome.

var temparray
var tempposition

let processCache = function (array, position) {
  if (typeof array === 'undefined') {
    array = []
  }
  if (array.length > 0) {
    let chunk = array.shift()
    temparray = array
    tempposition = position
    caches.open('testing')
      .then((cache) => { cache.addAll([chunk]) })
      .then(function () { processCache(temparray, tempposition) })
  }
}

回答1:

Option1: Using webpack

Webpack users can benefit from webpack-offline-plugin which is well maintained and does the job well. However, at runtime you will still need to write a little bit of code for dynamic caching. for dynamic caching, please refer to this advise by the plugin maintainer: here

Option2: Make your own

Register The Service Worker in your main bundle file

assumptions: your service worker file called sw.js and it's in the root folder of your website.

if ('serviceWorker' in navigator) {
  // Recommended to register onLoad
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js');
  });
}

inside sw.js: install statics

self.addEventListener('install', function(event) {
  self.skipWaiting();
  event.waitUntil(
    caches.open('static-files-v1').then(function(cache) {
      return cache.addAll(['/', 'someFile.js', 'someFile.css']);
    })
  );
});

inside sw.js: fetch installed statics

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request)
    })
  );
});

More Advanced Steps

inside sw.js: cache dynamic URLs on fetch

fetch event will be listening to anything and everything being fetched into your website through HTTPS, please read my comments to help understand the snipper.

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('dynamic-content-v1').then(function(cache) {
      // check if the requested URL is inside the dynamic-content-v1
      return cache.match(event.request).then(function (response) {
        // when found, respond with it.
        // when not found: return it as it is after taking a clone
        // and storing it, so next visit to the URL it will be there
        return response || fetch(event.request).then(function(response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

This method is not a very good practice to do by default specially if you have thousands of pages, content and images. You don't want to bloat the user and use all your quota of cache storage. please consider caching only what matters to be shown first and keep it fresh as you don't want to ship outdated info.

Option3 and important note:

IMPORTANT: You do not need SW to add dynamic content to the cache API. they both 2 different things. Feel free to fetch and add to cache API from inside your frameworks components \ async routes or whatever.

suggestions and recommendations about your fiddle:

I'm not quite sure I understand what are you doing 100%. I can see a recursive function but not sure why do you need it or what's going in there. Here's a snippet on how to cache dynamically from inside your component:

// in case browser doesn't support cache
var URL_2_CACHE = '/content/important.html';
if (!('caches' in window)) return;
fetch(new Request(URL_2_CACHE)).then(function(response){
return caches.open('dynamic-cahce-v1').then(function(cache) {
    // DONE!
    return cache.put(URL_2_CACHE, response);
  });
});

Now that the HTML page has been stored in cache, SW will fetch it from there on next request.



回答2:

You said you're already familiar with sw-precache so I would recommend using sw-precache-webpack-plugin as you mention you are using.

Use the runtimeCaching to specify different caching strategies for the content that you want available offline.

If your caching too many things, use the maxEntries inside the runtimeCaching config like so:

new SWPrecacheWebpackPlugin({
  runtimeCaching: [{
    urlPattern: /\/media\//,
    handler: 'cacheFirst',
    options: {
      cache: {
        maxEntries: 100,  // <-- limit entires to 100 before recycling
        name: 'media-cache'
      }
    }
  }]
})


回答3:

If you are using service worker then you just need to get service-worker object and register the service worker file.

I have done some code in this: offline experience. Here's the code of that repo, hope it helps.

if ('serviceWorker' in navigator) {
  // registering the service worker file
  navigator.serviceWorker.register('./service-worker.js', {
    scope: './'                                                     // optional
  }).then(function (reg) {
    console.log('Service Worker registered successfully!');
  }).catch(function (err) {
    console.log('Service Worker failed to register', err);
  });
}

var name = 'simple nodexperts cache v2',
    filesToCache = [
      './index.html',
      './css/style.css',
      './script.js',
      './service-worker.js'
    ];

self.addEventListener('install', function (event) {
  console.log("installing");
  event.waitUntil(
    // opens cache
    // cache is an object which is available inside service-worker
    caches.open(name).then(function (cache) {
      return cache.addAll(filesToCache);
    })
  );
});

self.addEventListener('fetch', function (event) {
  console.log("fetching");
  event.respondWith(
    fetch(event.request).catch(function() {
      return caches.match(event.request);
    })
  );
});

For more you can check these blogs too, they are really good reads:

  • https://www.smashingmagazine.com/2016/02/making-a-service-worker/
  • https://www.nodexperts.com/blog/5-things-need-know-javascript-service-workers/