I would like to benefits from offline capabilities, so I m making sure to cache on install the page it self and other assets like css and js
self.addEventListener('install', function (event) {
event.waitUntil(caches.open(cacheName).then(cache => cache.addAll([
"/",
self.location.href
])));
self.skipWaiting();
});
as install will be run only once for the worker, how can I make sure that those assets get refreshed on every run or at least from time to time?
You can achieve this also by defining a new cache name each time you deploy a new version of your website:
//change cache name with each deploy
var staticCacheName = 'NewWithEachDeploy';
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open(staticCacheName).then(function (cache) {
return cache.addAll([
'index.html',
'...'
]);
})
);
});
Then on activate you delete all the old caches.
self.addEventListener('activate', function (event) {
event.waitUntil(caches.keys().then(function (keyList) {
return Promise.all(keyList.map(function (cacheKey) {
//delete all caches which do not equal with the current cache name (staticCacheName)
if (cacheKey !== staticCacheName) {
console.log('[ServiceWorker] Removing old cache', cacheKey);
return caches.delete(key);
}
}));
}));
});
This way you make sure all resources are updated at once. Otherwise you can get in trouble. For example your cached html is updated to the newest version but the JS file is still old and this constellation probably does not play well together.
While implementing cache expiration using vanilla JS can be bit challenging, you can try using Workbox by Google. It has this concept of cache expiration.
A super basic example:
const cacheExpirationPlugin = new workbox.cacheExpiration.CacheExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 24 * 60 * 60
});
You could read more about it here.
Related
I'm trying to get a basic service worker-based updating capability going for my web app.
(You'll see in the code below that I clear the cache by specific name - 4.7 - and by dynamic name. This is because it seems to me that the code I'm using clears the NEW cache name, not the old one. Maybe a separate issue. Right now I'm clearing both.)
If I change the cacheName and the annotateAccount.js file (this running on localServer) I can see that the service worker does its job: downloads a new version from the server (I see a status of "200 OK" in the console vs. "200 OK (from service worker)" when running a page refresh without an update to the service worker file.
Even though the code downloads a new version of the JS, my actual app (running in Chrome) pulls from an old cached version of the file.
If I "empty cache and hard reload" I get the new version. If I immediately do a regular refresh I get the old (browser cached) version of the file.
I've read enough to know there is browser caching on top of service worker caching, but what's the F-ing point of using a service worker if you can't overcome the browser's caching? I'm using XAMMP/Apache to run my localhost dev environment. I haven't tried this in a staging or production environment on my actual servers. Since I seem to be fighting Chrome's local caching features I'm not sure that matters.
I'd previously just put "?v=xxx" after filenames throughout my app, and changed the parameter when I released updates, in an attempt to give users the latest code. This doesn't work well.
What am I missing? Any direction/help appreciated.
Here's my service worker JS file
var urlsToCache = [
'annotateAccount.html',
'annotateAccount.js',
'web_background.js'
];
const cacheName = '4.8';
self.addEventListener('install', event => {
console.log('Install event...', urlsToCache);
event.waitUntil(
caches.open(cacheName)
.then(function(cache) {
console.log('Opened cache', cacheName);
return cache.addAll(urlsToCache);
})
);
});
var url;
self.addEventListener('fetch', (event) => {
//Cache falling back to network
url = event.request.url.split(".").pop(); // check php
if (url !== "php"){
event.respondWith(
caches.match(event.request)
.then((response) => {
return response || fetch(event.request);
})
);
} else { // END check for PHP - skip trying cache/SW.
console.log('Trying a php file - always let code run it...');
}
});
self.addEventListener('message', function (event) {
console.log('ServiceWorker cache version: ', cacheName, event);
console.log('Received msg1: ', event.data.action);
if (event.data.action === 'skipWaiting') {
console.log('ccClearing cache: ', cacheName);
caches.delete(cacheName); // actually removes cached versions
caches.delete("4.7"); // delete NAMED cache...
self.skipWaiting();
} else {
console.log('Do not clear cache...');
}
});
I'm building my first progressive web application. I'm trying to have delete cache button, is there a way to delete all files excluding certain files & also the ones caches by the service work also.
I seen caches.delete() been used but can i excluding files using that?
delete cache:
self.caches.keys().then(keys => { keys.forEach(key => console.log(key)) })
self.caches.delete('demo')
service worker:
var cacheFiles = [
'/',
'/index.html',
'/style.css'
];
self.addEventListener('install', function(e) {
e.waitUntil(
caches.open("demo").then(function(cache) {
return cache.addAll(cacheFiles);
})
);
});
I gather from your question that you want to delete only certain files from a cache.
I think that's possible, using something like this:
function deleteFileFromCache(fileName, cacheName) {
caches.open(cacheName).then(function(cache) {
cache.delete(fileName).then(function(response) {
console.log('cache.delete response:', response);
}
}
At least the MDN docs imply this will work.
I have built PWA enabled SPA by using Vue, js, node technology. Problem is that service worker deletes my stored cache for viewing app offline whenever i switch off my PC, otherwise it works. Second problem is that it refuses to fetch data that includes google fonts, unless I enable CORS in the browser. Since PWA is based on the browser and users will not have CORS add-on installed in the browser, is there some way to enable CORS on the (Windows) server? Thanks in advance, here is my service worker code.
// service worker file. Every time when you change this file rename staticCache const in order to changes to be visible when user closes tab and reopens it.
const staticCache = 'site-static-1'; // static cache for main site files, app shell (all static files, html, css, starting images, logo etc). If you change code always rename this to new number
const assets = [ // this is array of app shell API requests for assets. Those are keys and asset values (images etc) will be values of key/value pair in array
'/',
'/index.html',
'/app.c328ef1a.js',
'/app.c328ef1a.css',
'/manifest.webmanifest',
'/photo-login.04703ebf.jpg',
'/icon_area.9bfa0c9a.png',
'/icon_144x144.c75152b5.png',
'/img/avatar_default.png',
'/img/icon_pwa.png',
'https://fonts.googleapis.com/css?family=Raleway:300,400,600,700',
'https://code.highcharts.com/stock/highstock.js',
'https://code.highcharts.com/stock/modules/data.js',
'https://code.highcharts.com/stock/modules/exporting.js',
'https://cdn.pubnub.com/sdk/javascript/pubnub.4.21.7.min.js',
'https://fonts.gstatic.com/s/raleway/v14/1Ptrg8zYS_SKggPNwIYqWqhPAMif.woff2',
'https://fonts.gstatic.com/s/raleway/v14/1Ptug8zYS_SKggPNyCMIT5lu.woff2',
'https://fonts.gstatic.com/s/raleway/v14/1Ptug8zYS_SKggPNyC0ITw.woff2',
'https://fonts.gstatic.com/s/raleway/v14/1Ptrg8zYS_SKggPNwPIsWqhPAMif.woff2',
'https://fonts.gstatic.com/s/raleway/v14/1Ptrg8zYS_SKggPNwPIsWqZPAA.woff2',
'https://fonts.gstatic.com/s/raleway/v14/1Ptrg8zYS_SKggPNwJYtWqhPAMif.woff2',
'https://fonts.gstatic.com/s/raleway/v14/1Ptrg8zYS_SKggPNwJYtWqZPAA.woff2'
]; // we hve to fetch separately fonts from links inside fonts.googleapis.com
// In Chrome Web Tools go to Application>Cache storage and click /css?family=raleway, links are inside value of that key
// installing service worker event
self.addEventListener('install', evt => {
console.log('Service worker has been installed');
// programmatically skip awaiting for new changed sw file to become active, because sometimes closing Chrome and tabs is not enough
// if we change sw.js and want to make sure change is visible ie cache is refreshed, we need to change version number of staticCache constant.
// NOTE: If we save this file by adding asset to be fetched (image for example) it will be visible in a new cache upon clicking browser reload.
// ..but if we delete it from the list of items to be fetched, IT WILL REMAIN in the cache until we change staticCache version number, save and reload browser page.
// So it is best practice to always change version number in staticCache whenever you make and save changes.
self.skipWaiting(); // it will be triggered only if there is a new sw version that awaits to be executed
evt.waitUntil( // installing awaits until this is executed first, otherwise it could stop it
caches.open(staticCache).then(cache => { // it opent cache, if there isn't it will create one
cache.addAll(assets); // add into cache all assets from the assets array []
})
);
});
// activating service worker event
self.addEventListener('activate', evt => {
console.log('Service worker has been activated');
evt.waitUntil(
caches.keys().then(keys => { // get array with keys (of key/value pair) from different cache versions in Chrome Dev Tools>Application>Cache Storage
// go thru all caches keys array and delete all values except newest cache, named in staticCache const. That way only the last cache is used by an app
return Promise.all(keys
.filter(key => key !== staticCache)
.map(key => caches.delete(key))
)
})
);
});
// fetch event
self.addEventListener('fetch', evt => {
console.log('SW is fetching data');
evt.respondWith( // check if requested data is stored in the cache.
caches.match(evt.request).then(cacheRes => {
return cacheRes || fetch(evt.request) // if item is in cache use it, if isn't go to the server and fetch it
})
)
});
Seems like the issue is with the activate event handler in the service worker. This is mainly using for removing the old cache from the browser. Try replacing the event listener with the following code
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cache) => {
if (cache !== staticCache) {
return caches.delete(cache); //Deleting the old cache (cache v1)
}
})
);
})
.then(function () {
console.info("Old caches are cleared!");
return self.clients.claim();
})
);
});
I'm developing a reactjs based application. I also made service-worker settings on it. After add to home screen , application never checks the server for new updates.
I also tried:
window.location.reload(true);
But it doesn't update new version.
I'm using Apache server to serve build folder and for update I'm getting a new build of my project and serve that on Apache server.
I finally resolved my problem after two days. The problem was in service-worker file. I had to add event listener if page reloaded and server files had changes so it will update the files.
So I added this section to serviceWorker.js in register function:
window.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
});
Just don't forget. This listener call when page is reload. So I make API service to check there is new version or not. if there is new version , It have to reload the page to get new files.
this question was so helpful: How to clear cache of service worker?
Update (December.1.2019):
I found better way to update new PWA. Actually that way (above) not work on iOS 13. So I decide check update by API. PWA Send current version to API and if there is new version released , in PWA we should delete all caches:
caches.keys().then(function(names) {
for (let name of names)
caches.delete(name);
});
And after that reload application:
window.location.href = "./";
After reload because there is no cache to load pages on offline mode, so PWA will check server and get new version.
this work for me:
src/index.tsx
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register({
onUpdate: (e) => {
const { waiting: { postMessage = null } = {} as any, update } = e || {};
if (postMessage) {
postMessage({ type: 'SKIP_WAITING' });
}
update().then(() => {
window.location.reload();
});
},
});
My team and I have a project that was originally built as a PWA, but have since decided to scrap that idea as we realized it would need to change much more frequently than originally intended. However, the service worker is already live, as well as a newly redesigned landing page for the website. Despite all our efforts to clear the PWA caching, our clients are still reporting that they are receiving the old cached version of the website.
Currently, we have the service worker set up to delete all caches upon install (and whenever anything at all happens as a precaution), as well as some JavaScript to unregister the service worker when the new page actually loads. However, the problem is that none of this runs until the user makes a request to the website, and at that point the browser is already loading the cached content. Is it possible to clear this cache and prevent the browser from loading any content that was already cached?
Current service-worker.js
// Caching
var cacheCore = 'mkeSculptCore-0330121058';
var cacheAssets = 'mkeSculptAssets-0330121058';
self.addEventListener('install', function (event) {
self.skipWaiting();
caches.keys().then(function (names) {
for (let name of names)
caches.delete(name);
});
});
self.addEventListener('activate', function (event) {
caches.keys().then(function (names) {
for (let name of names)
caches.delete(name);
});
});
self.addEventListener('fetch', function (event) {
caches.keys().then(function (names) {
for (let name of names)
caches.delete(name);
});
});
Script in index.html
(function () {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
//returns installed service workers
if (registrations.length) {
for (let registration of registrations) {
registration.unregister();
}
}
});
}
})();
So far, I've read a few other similar StackOverflow answers, including this one, but they tend to rely on users manually doing something to fetch the new content, ie. via a hard reload or disabling the service worker manually through the browser settings. However, in my case, we cannot rely on manual user actions.
One way to solve this issue is to add a timestamp at end of the file(js, css) name so each time when it is making a request, the cache key is not available in the service worker and thus it tends to get a new version of the file at each load.
<script type="text/javascript" src="/js/scipt1.js?t=05042018121212"/>
For appending a new timestamp dynamically in the file name, please check this answer
But this may not be reliable if HTML itself is cached.
add this before to "update" all contents:
$.each(['index.html','file1.js','file2.js','file3.js'],function(index,file) {
$.get(file+'?t='+new Date().getTime(), function(){});
});
location.reload(true);
for the ServiceWorker to stop all windows using it must be closed.
If it's a webapp you can use window.close();
This code just loads a fresh version of the files in the list.
If there are any internal caches they will be all updated.