Service Worker Cache - javascript

I have the following code wich it should cache 3 files but wen I reload the page and I enter dev tool and network it says that all my files are from service worker.It my code correct and only those 3 files are catched or all the file are catched?
let cache_name = 'v1'
let cache_files = [
'./style/general.css',
'./style/authenticate.css',
'./javascript/app.js'
];
self.addEventListener('install',function(e){
console.log('[ServiceWorker] installed')
e.waitUntil(
caches.open(cache_name).then(function(cache){
console.log('[ServiceWorker] Caching ',cache_files)
return cache.addAll(cache_files)
})
)
});
self.addEventListener('activate',function(e){
console.log('[ServiceWorker] activated')
e.waitUntil(
caches.keys().then(function(cache_names){
return Promise.all(cache_names.map(function(this_cache_name){
if(this_cache_name !== cache_name){
console.log('[ServiceWorker] removing cached files from',this_cache_name)
return caches.delete(this_cache_name)
}
}))
})
)
})
self.addEventListener('fetch',function(e){
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
})
And how can I tell which files are cached and which one are not?

I don't completely understand your question.
Your code does these things:
On installation the three files in the array are put in a cache named "v1"
On future activations the cache is iterated and all items in the cache that don't belong to the cache name cache_name are removed (that is, if you change the cache_name to "v2" in the future, all "v1" entries are removed automatically)
On fetch events from the client the cache is checked for the requested file; if the file is found from the cache, the SW returns that, if not, the SW forwards the request to the network
You can inspect the cached files in Chrome Dev tools via the "Application" tab's "Cache Storage" pane on the left side. Check this out and scroll to the bottom: https://developers.google.com/web/tools/chrome-devtools/manage-data/local-storage

Related

Why doesn’t my service worker store all the items during the “installation” event?

This is the code for my service worker for the “installation” event:
const CACHE_NAME = "static3";
self.addEventListener("install", function (event) {
event.waitUntil(
caches.open(CACHE_NAME).then(function (cache) {
cache.addAll([
"/",
"/blog/",
"/links/",
"/about/",
"/contact/",
"/privacyPolicy/",
"/offline/",
"/css/CP.ttf",
"/css/CPItalic.ttf",
"/css/WS.ttf",
"/css/WSItalic.tff",
"/css/style.css",
"/css/typography.css"
]);
})
);
});
I tried several times but the service worker doesn’t store all the items in the cache. This is a screenshot from Google Chrome’s “Application” tab:
Why are only some items stored in the cache? Keep in mind that all the paths to the other files are correct.
I found the error. As you can see, I accidentally wrote "/css/WSItalic.tff" instead of "/css/WSItalic.ttf". This caused the “install” event to reject all of the items, since it stores them unless there’s even a single error in the paths. So, the “install” event rejected the files to store in the cache and only the fetch requests from the page I was viewing were stored.

How to make persistent PWA cache?

I've been trying to make offline only PWAs for Android, but the site's cache keeps clearing every so often. Is there any way to make the cache stay permanently?
You can define caching strategies for static assets and data requests for your service worker.
In the following article about service workers and caching strategies I list the different strategies and describe when it makes more sense to implement a specific one.
You can cache static assets and provide them offline when the SW is installing. Those files should be only the "minimum" version of your app (usually called app shell). Because of this, the cache.addAll method rejects the promise if it is not possible to get any of the resources. This means the service worker will install successfully only if all the targeted resources are cached.
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('staticAssetsCache').then(function(cache) {
return cache.addAll(
[
'/css/bootstrap.css',
'/css/styles.css',
'/js/jquery.min.js',
'/offline.html'
]
);
})
);
});
You can also cache HTTP GET Requests, for example below the stale while revalidate strategy that returns the data from the cache, if available, and in the background attempts to fetch and cache a newer version from the network:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('www.my-web-app.com')
.then(function(cache) {
return cache.match(event.request)
.then(function(response) {
var fetchPromise = fetch(event.request).then(function(networkResponse) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
})
// response contains cached data, if available
return response || fetchPromise;
})
})
);
});
If you are using Angular or Workbox library, https://dev.to/paco_ita/create-progressive-web-apps-with-angular-workbox-pwa-builder-step-4-27d for more details.
I believe I read somewhere iOS Safari and Chrome would invalidate the cache frequently to get new updates. No logic behind it, just re-fetching the files.
Solution:
(In a recent Chrome devlog, it mentions a reduction in frequency from 3 days to 1)
to prevent the clearing of the cache / IndexDB I found this.
if (navigator.storage && navigator.storage.persist)
// '.persist()' will silently pass or trigger a dialog
navigator.storage.persist().then(function(persistent) {
alert(persistent ? 'persistent' : 'denied');
})
else
alert('not available - iOS / ancient Android?');

Service Worker: How to cache the first (dynamic) page

I have this one-page app with a dynamic URL built with a token, like example.com/XV252GTH and various assets, like css, favicon and such.
Here is how I register the Service Worker:
navigator.serviceWorker.register('sw.js');
And in said sw.js, I pre-cache the assets while installing:
var cacheName = 'v1';
var cacheAssets = [
'index.html',
'app.js',
'style.css',
'favicon.ico'
];
function precache() {
return caches.open(cacheName).then(function (cache) {
return cache.addAll(cacheAssets);
});
}
self.addEventListener('install', function(event) {
event.waitUntil(precache());
});
Note that the index.html (that registers the Service Worker) page is just a template, that gets populated on the server before being sent to the client ; so in this pre-caching phase, I'm only caching the template, not the page.
Now, in the fetch event, any requested resource that is not in the cache gets copied to it:
addEventListener('fetch', event => {
event.respondWith(async function() {
const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse;
return fetch(event.request).then(updateCache(event.request));
}());
});
Using this update function
function updateCache(request) {
return caches.open(cacheName).then(cache => {
return fetch(request).then(response => {
const resClone = response.clone();
if (response.status < 400)
return cache.put(request, resClone);
return response;
});
});
}
At this stage, all the assets are in the cache, but not the dynamically generated page. Only after a reload, can I see another entry in the cache: /XV252GTH. Now, the app is offline-ready ; But this reloading of the page kind of defeats the whole Service Worker purpose.
Question: How can I send the request (/XV252GTH) from the client (the page that registers the worker) to the SW? I guess I can set up a listener in sw.js
self.addEventListener('message', function(event){
updateCache(event.request)
});
But how can I be sure that it will be honored in time, ie: sent by the client after the SW has finished installing? What is a good practice in this case?
OK, I got the answer from this page: To cache the very page that registers the worker at activation time, just list all the SW's clients, and get their URL (href attribute).
self.clients.matchAll({includeUncontrolled: true}).then(clients => {
for (const client of clients) {
updateCache(new URL(client.url).href);
}
});
Correct me if I understood you wrong!
You precache your files right here:
var cacheAssets = [
'index.html',
'app.js',
'style.css',
'favicon.ico'
];
function precache() {
return caches.open(cacheName).then(function (cache) {
return cache.addAll(cacheAssets);
});
}
It should be clear that you cache the template since you cache it before the site gets build and this approach is not wrong, at least not for all types of files.
Your favicon.ico for example is a file that you would probably consider as static. Also, it does not change very often or not at all and it isn't dynamic like your index.html.
Source
It should also be clear why you have the correct version after reloading the page since you have an update function.
The solution to this problem is the answer to your question:
How can I send the request (/XV252GTH) from the client (the page that registers the worker) to the SW?
Instead of caching it before the service-worker is installed you want to cache it if the back end built your web page. So here is how it works:
You have an empty cache or at least a cache without your index.html.
Normally a request would be sent to the server to get the index.html. Instead, we do a request to the cache and check if the index.html is in the cache, at least if you load the page for the first time.
Since there is no match in the cache, do a request to the server to fetch it. This is the same request the page would do if it would load the page normally. So the server builds your index.html and sends it back to the page.
After receiving the index.html load it to the page and store it in the cache.
An example method would be Stale-while-revalidate:
If there's a cached version available, use it, but fetch an update for next time.
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('mysite-dynamic').then(function(cache) {
return cache.match(event.request).then(function(response) {
var fetchPromise = fetch(event.request).then(function(networkResponse) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
})
return response || fetchPromise;
})
})
);
});
Source
Those are the basics for your problem. Now you got a wide variety of options you can choose from that use the same method but have some additional features. Which one you choose is up to you and without knowing your project in detail no one can tell you which one to choose. You are also not limited to one option. In some cases you might combine two or more options together.
Google wrote a great guide about all the options you have and provided code examples for everything. They also explained your current version. Not every option will be interesting and relevant for you but I recommend you to read them all and read them thoroughly.
This is the way to go.

Why is my service worker only caching visited pages?

Im currently developing a simple PWA with an index file that contains an inputfield where you can enter your medication barcode and hit submit. A PHP-file will then be displayed showing some information about the drugs recieved from a MySQL database.
Im now trying to cache the app shell in order to load the necessairy resources (html, css, scripts etc.) once the app is working offline. I kinda succeeded in this, but it only seems to work when the page that you're trying to cache is already visited once when the app was online. And this only is the case when I register that same service-worker on the result page, not only on the index page.
Apparantly, this kind of caching in which only the visited pages are cached is called 'partial' caching by my professor. I don't want this partial caching, I want a complete caching in which the service-worker is registered once on the index file and then caches all the necessairy files into to cache so that I can use it when offline.
This is how my service-worker looks right:
var cacheName = 'pillexplainerPWA';
var filesToCache = [
'http://localhost:80/MedicationProject/',
'http://localhost:80/MedicationProject/index.html',
'http://localhost:80/MedicationProject/result.php',
'http://localhost:80/MedicationProject/result2.html',
'http://localhost:80/MedicationProject/scripts/app.js',
'http://localhost:80/MedicationProject/styles/main.css',
'http://localhost:80/MedicationProject/styles/result.css',
'http://localhost:80/MedicationProject/images/back-button.jpg',
'http://localhost:80/MedicationProject/images/barcode.jpg'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.log('[ServiceWorker] Fetch', e.request.url);
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});
Can someone explain to me what I'm doing wrong and what needs to be adjusted to this code so that it won't cache only what is visited but everything the moment that index.html is loaded? The strange thing is, when I look into the cache when index.html is fired..it does show me all my files being there. But when I go offline and then hit that submit button to go to the result page, it doesn't load the cache.

Avoid caching start_url with service worker

I'm having some problems on setting up a service worker for my website.
I only want to cache css/js/fonts and some images/svg, I don't want to cache the HTML since all of it is updated every minute.
It kinda works, but trying on my smartphone I keep getting the notification "Add to homescreen" even when I've already added it. And on the Chrome Dev app I don't get the Add button.
Also with the Lighthouse I get the following errors:
"Does not respond with a 200 when offline"
"User will not be prompted to Install the Web App, Failures: Manifest start_url is not cached by a Service Worker."
Right now my sw.js is like this. As you can see I commented the fetch part because it was caching the HTML and also the Cookies weren't working.
Is there around a simple Service Worker "template" to use?
const PRECACHE = 'app-name';
const RUNTIME = 'runtime';
// A list of local resources we always want to be cached.
const PRECACHE_URLS = [
'/css/file.css',
'/js/file.js',
'/images/logo.png',
'/fonts/roboto/Roboto-Regular.woff2'
]
// The install handler takes care of precaching the resources we always need.
self.addEventListener('install', event => {
event.waitUntil(
caches.open(PRECACHE)
.then(cache => cache.addAll(PRECACHE_URLS))
.then(self.skipWaiting())
);
});
// The activate handler takes care of cleaning up old caches.
self.addEventListener('activate', event => {
const currentCaches = [PRECACHE, RUNTIME];
event.waitUntil(
caches.keys().then(cacheNames => {
return cacheNames.filter(cacheName => !currentCaches.includes(cacheName));
}).then(cachesToDelete => {
return Promise.all(cachesToDelete.map(cacheToDelete => {
return caches.delete(cacheToDelete);
}));
}).then(() => self.clients.claim())
);
});
// The fetch handler serves responses for same-origin resources from a cache.
// If no response is found, it populates the runtime cache with the response
// from the network before returning it to the page.
self.addEventListener('fetch', event => {
// Skip cross-origin requests, like those for Google Analytics.
// if (event.request.url.startsWith(self.location.origin)) {
// event.respondWith(
// caches.match(event.request).then(cachedResponse => {
// if (cachedResponse) {
// return cachedResponse;
// }
// return caches.open(RUNTIME).then(cache => {
// return fetch(event.request).then(response => {
// // Put a copy of the response in the runtime cache.
// return cache.put(event.request, response.clone()).then(() => {
// return response;
// });
// });
// });
// })
// );
// }
});
I'm not sure why the install banner appears but the two errors given by lighthouse are related to the missing caching of the very start_url, propably index.html. So Lighthouse will always be telling you about those if you follow the caching strategy you described here.
I suggest you could try Workbox and their runtime caching. Runtime caching, in a nutshell, works like so: you specify urls like *.svg, *.css etc. and the SW caches them once the client first asks them. In the future, when the files are already cached, the SW serves them from the cache to the client. Basically you tell the SW to cache this and that kind of urls when it encounters them and not in advance.
Runtime caching could very well be accompanied by precaching (that may also be found from Workbox!) to cache a bunch of files.
Check it out here: https://workboxjs.org
They have a couple of examples and plugins for build tooling.

Categories

Resources