reload cashed files to browser serviceWorker in JavaScript - javascript

Every time that I edit my PWA App source, Like html or js files, I have to manually delete browser cached files and history in order to load new data that i uploaded.
my serviceWorker routine try to cache my main url html only but it seems all files being cached automatically.
here is my serviceWorker file:
var cacheName = 'app-pwa';
var filesToCache = [
'/',
'/index.html'
];
/* Start the service worker and cache all of the app's content */
self.addEventListener('install', function(e) {
e.waitUntil(
caches.open(cacheName).then(function(cache) {
return cache.addAll(filesToCache);
})
);
});
/* Serve cached content when offline */
self.addEventListener('fetch', function(e) {
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});
So anyone can help me how to refresh cached files in browser?
thank you

well I find the way myself and will share here for anyone who may need it.
the way is to define versions in the end of cache file name:
var cacheName = 'app-pwa-v1';
and increase version for any update. and define another service worker to delete previous cached files.
self.addEventListener('activate', (e) => {
e.waitUntil(caches.keys().then((keyList) => {
Promise.all(keyList.map((key) => {
if (key === cacheName) { return; }
caches.delete(key);
}))
})));
});
this will delete old cached files after second app startup.

Related

How to force update PWA cache after login [JS]?

I have a simple forum that I want to make work offline. I have a dynamic and static cahce. The static cache fills on the install event and the dynamic as you go allong and look at posts.
The problem is that the pages it caches include the header where you have a link to the profile if you are logged in and link to registration page if you are not logged in.
After logging in it still shows the registration link instead of the profile link. The way to fix it would be to refresh the cache?
Is there a way to do this or is there some other fix for this type of issue(besides network first approach)?
I am relativly new to PWAs and I can't find any useful tips.
My service worker looks like this:
const staticCacheName = "ScroocCacheV1";
const dynamicCacheName = "ScroocDynamicCacheV1";
const assets = [
'/',
'/css/main_styles.css',
'/js/ui.js',
'/about',
'/policies',
'/register',
'/createTopic',
'/stats',
'/proposals',
];
const limitCacheSize = (name, size) => {
caches.open(name).then(cache => {
cache.keys().then(keys => {
if(keys.length > size) {
cache.delete(keys[0]).then(limitCacheSize(name, size));
}
});
});
}
const dynamicCacheLimit = 18;
// Install service worker
self.addEventListener('install', evt => {
evt.waitUntil(
caches.open(staticCacheName).then(cache => {
cache.addAll(assets);
})
);
});
// Activate event
self.addEventListener('activate', evt => {
evt.waitUntil(
caches.keys().then(keys => {
keys.map((key => {
if (key !== staticCacheName && key !== dynamicCacheName) {
return caches.delete(key); //Deleting the old cache (cache v1)
}
}))
})
)
});
// Intercept fetch
self.addEventListener('fetch', evt => {
evt.respondWith(
fetch(evt.request).then(fetchRes => {
return caches.open(dynamicCacheName).then(cache => {
return caches.match(evt.request).then(function(result) {
if (result) {
return result;
} else {
cache.put(evt.request.url, fetchRes.clone());
limitCacheSize(dynamicCacheName, dynamicCacheLimit);
return fetchRes;
}
});
});
}).catch(function() {
return caches.match(evt.request).catch((error) => {
console.log(error)
return caches.match('/img/fallbackImage.png');
});
})
);
});
This worked for me!
Before you could attempt to empty the cache, the service worker must first be successfully installed. So for the record, your sw.js file should begin with the usual
self.addEventListener("install", ...etc
Now this is where we get to cleaning up. Create a variable to store the name of the cache you wish to purge/update (makes targeting different caches easier)
var TargetCache= 'NameOfCacheToClean';
Next, add an EventListener that triggers each time the service worker is activated (the activate-event occurs on page reload/refresh)
self.addEventListener('activate', event =>
{
const currentCaches = [TargetCache];
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())
);
});
Just in-case, i normally add the event listener that intercepts the outgoing fetch-requests after the code that clears the old cache.
self.addEventListener('fetch', function(event) {...etc
The way to fix it would be to refresh the cache?
That is correct, assuming you cached the path /login, the service worker will always display what was cached under that path, based on your code.
Is there a way to do this or is there some other fix for this type of issue(besides network first approach)?
It's not really something to "fix", what you described is somewhat expected behaviour.
There are several ways around this tho, network first is just one:
use a message to update cache on login
use different urls or url parts like query to skip cache when user is logged in
hide the UI that you don't need on the client depending on user state
Probably many more.

ServiceWorker doesn't load matched resources

I am implementing a service worker on my web application.
This is my serviceWorker.js:
var CACHE_NAME = 'my-cache';
var urlsToCache = [
'img/logout.png'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME).then(function(cache) {
return cache.addAll(urlsToCache);
}))
});
self.addEventListener('fetch', function(event) {
console.log(event.request);
caches.match(event.request).then(function(response) {
if (response) {
console.log(response);
return response;
}
});
});
It's all woriking fine (in dev tools i can see the png file stored is the cache storage).
The problem is when i go offline: i guess that the get request for the png file is matched because if i navigate to localhost:8080/myapp/img/logout.png the response logged (as you can see in the script above) it'is not undefined but chrome still returns ERR_INTERNET_DISCONNECTED.
This is the dir structure of the webapp:
here
What am i doing wrong?
In fetch event of service worker Instead of matching request object change it to cache.match(event.request.url).

How to make a service worker work offline?

I can't get my service worker to work offline. No matter what tutorial I use.
I registered the service worker in my index.html file like:
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/serviceworker.js')
.then((reg) => console.log('success: ', reg.scope))
.catch((err) => console.log('Failure: ', err))
})
}
</script>
The serviceworker.js looks like:
const CACHE_NAME = "version-1"
const urlsToCache = [ 'index.html' ]
const self = this
// Install Service Worker
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache')
return cache.addAll(urlsToCache)
})
)
})
// Activate Service Worker
self.addEventListener('activate', (event) => {
const cacheWhitelist = []
cacheWhitelist.push(CACHE_NAME)
event.waitUntil(
caches.keys().then((cacheNames) => Promise.all(
cacheNames.map((cacheName) => {
if(!cacheWhitelist.includes(cacheName)) {
return caches.delete(cacheName)
}
})
))
)
})
I'm not sure what I've forgotten or what mistake I have. The serviceworker.js is right beside the index.html, manifest.json etc.
I sometimes get an error with "An unknown error occured when fetching the script".
Kind regards
Your service worker only contains code to create a cache and store the HTML file in that local cache. This cache is managed by you, the browser does not care about it when fetching web pages normally.
When your browser fetches that index.html web page, it does not know about that cache. So what you need to do is to intercept that fetch. For this, you need to register an event listener for the fetch event and respond with your cache. The browser will then use that file instead of sending a request to the server.
self.addEventListener('fetch', event => {
if (event.request.method != 'GET') return;
event.respondWith(async function() {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(event.request);
// If no cached version, fall back to server fetch
return cached ? cached : fetch(event.request);
})
});

PWA - empty service worker precache runtime

I'm developing an audio-based PWA and, since I'm not familiar with this technology, I have a couple of doubts regading the cache management and invalidation in the service worker.
The application need to work offline, that I covered using a SW precache.
My only doubt is the amount of data: in the experience there are 5 use case scenarios. Each scenario has ~30MB of audio content, that means around 150MB + all images, js and css in total to precache.
I know that this exceeds the limit of some browsers (se this question and this article)
and in general you must be careful with the storage size, that also depends on the user's device available space on disk.
So that's what I thought: since between one scenario and another, the users will stop by a desk with WiFi connection, my idea is to empty the cache runtime after an user's action (like pressing a button), and replace it with thw new content.
This way I would store only one scenario at a time, that means ~35MB, a reasonable size.
Do you think that's a good approach?
What's the best way to implement this?
Here's my current code:
service-worker.js
const PRECACHE = 'precache-test-v1';
// A list of local resources we always want to be cached.
const PRECACHE_URLS = [
'/',
'/audio/scenario1.mp3',
'/audio/scenario2.mp3',
'/audio/scenario3.mp3',
'/audio/scenario4.mp3',
'/audio/scenario5.mp3',
'/css/style.css',
'/js/bundle.js',
'/img/favicon.png',
'/img/logo.png',
'/img/image1.png',
'/img/image2.png',
'/img/image3.png',
'/img/image4.png',
'/img/image5.png',
];
// never cache these resources
const TO_SKIP = [/* empty for now */];
// The install handler takes care of precaching the resources we always need.
self.addEventListener('install', event => {
const now = new Date();
console.log(`PWA Service Worker installing - :: ${now} ::`);
event.waitUntil(caches.open(PRECACHE).then(cache => {
return cache.addAll(PRECACHE_URLS).then(() => {
self.skipWaiting();
});
}));
});
// The activate handler takes care of cleaning up old caches.
self.addEventListener('activate', event => {
const now = new Date();
console.log(`PWA Service Worker activating - :: ${now} ::`);
const currentCaches = [PRECACHE];
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.
self.addEventListener('fetch', event => {
// Skip cross-origin requests, like those for Google Analytics and the other provided urls.
if (event.request.url.startsWith(self.location.origin) && TO_SKIP.every(url => !event.request.url.includes(url))) {
event.respondWith(
caches.match(event.request).then(resp => {
return resp || fetch(event.request).then(response => {
return caches.open(PRECACHE).then(cache => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
}
});
index.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(registration => {
console.log('Registration successful, scope is:', registration.scope);
}).catch(error => {
console.log('Service worker registration failed, error:', error);
});
}
Thank you for your time,
Francesco
Hmm.. instead of precaching 5 videos, you could provide an button Save for offline so that the user can save only that videos that he wants to see later offline:
let videoUrl = url to that video:
button.addEventListener('click', function(event) {
event.preventDefault();
caches.open("myVideoCache").then(function(cache) {
fetch(videoUrl)
.then(function(video) {
cache.add(video);
});
});
});
Do delete 1 entry you need to open your cache and delete it. Pass the path that you stored.
caches.open('myVideoCache').then(function(cache) {
cache.delete('/path/to/audio.mp4').then(function(response) {
console.log("entry deleted");
});
})
More details you can find here: https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker

How to use a service worker in AMP files to cache files from a CDN?

I would like to a service worker to cache files coming from my cdn?
If files are under my root domain, everything is ok else I get always errors like "cross origin domain...".
All my static resources are on a cdn, how should I handle caching those files?
My code in AMP
<amp-install-serviceworker src="https://www.example.com/swAmp.js" layout="nodisplay"></amp-install-serviceworker>
My service worker swAmp.js
var CACHE_NAME = 'example-v0';
var urls = [
'https://static.example.com/img/menu/1.png',
'https://static.example.com/img/menu/2.png',
'https://static.example.com/img/menu/3.png'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urls);
})
);
});
All sample are based on local resources :(
Also how to serve them after?
A complete sample will be very helpful, thx.
I found an answer which works in this article
https://filipbech.github.io/2017/02/service-worker-and-caching-from-other-origins
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE)
.then(function(cache) {
console.log('Opened cache');
urls.forEach(function(value) {
const request = new Request(value, {mode: 'no-cors'});
fetch(request).then(response => cache.put(request, response));
});
return cache;
})
);
});

Categories