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) })
}
}
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.
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'
}
}
}]
})
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/