I have migrated my existing website to PWA.
My website is very simple made using HTML, JQuery and javascript.
Previously the caching mechanism was dependent on manifest.appcache.
Now when I try to cache the files using service worker none of the file are getting cached.
I have checked my service worker is registered and everything seems fine but files are not getting stored in cache.
I tried clearing the cache but no help.
I am sharing my service worker code below since it is production code I can not share all file names.
I used this site to learn PWA.
const staticCacheName = 'site-static-v4';
const dynamicCacheName = 'site-dynamic-v4';
const assets = [
"index.shtml",
"fallback.html",
"manifest.webmanifest"
];
// cache size limit function
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));
}
});
});
};
// install event
self.addEventListener('install', evt => {
//console.log('service worker installed');
evt.waitUntil(
caches.open(staticCacheName).then((cache) => {
console.log('caching shell assets');
cache.addAll(assets);
})
);
});
// activate event
self.addEventListener('activate', evt => {
//console.log('service worker activated');
evt.waitUntil(
caches.keys().then(keys => {
//console.log(keys);
return Promise.all(keys
.filter(key => key !== staticCacheName && key !== dynamicCacheName)
.map(key => caches.delete(key))
);
})
);
});
// fetch event
self.addEventListener('fetch', evt => {
//console.log('fetch event', evt);
evt.respondWith(
caches.match(evt.request).then(cacheRes => {
return cacheRes || fetch(evt.request).then(fetchRes => {
return caches.open(dynamicCacheName).then(cache => {
cache.put(evt.request.url, fetchRes.clone());
// check cached items size
limitCacheSize(dynamicCacheName, 15);
return fetchRes;
})
});
}).catch(() => {
if(evt.request.url.indexOf('.html') > -1){
return caches.match('fallback.html');
}
})
);
});
Below is the screenshot of my empty cache storage.
Related
My service worker is not rendering the offline.html page that I have created. It's just showing the real web page before clearing cache. After clearing cache it is showing the usual connection lost page in offline mode instead of showing the offline.html page I have created. I have unregistered it one time. That's the reason for it. I'm attaching the screenshot of serviceworker.js file here: serviceworker.js.
It is a react app I converted to PWA.
Here is my serviceworker.js:
const CACHE_NAME = "version-1";
const urlsToCache = ["index.html", "offline.html"];
const self = this;
//installing SW
self.addEventListener("install", event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log("Opened Cache")
return cache.addAll(urlsToCache)
})
)
})
//fetching request
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(() => {
return fetch(event.request)
.catch(() => caches.match("offline.html"))
})
)
})
//activating SW
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)
}
})
))
)
})
Am working on PWA and am trying to make my App works offline, so i cache my assets and its works fine, i could even use my app through my cached assets, BUT the problem is whenever i delete my cache using dev tools or whenever i update something in my assets my app not regenerate the cache again, how to regenrate the cache everytime when i start using the app online
Service worker registeration (app.js):
if('serviceWorker' in navigator){
navigator.serviceWorker.register('./sw.js')
.then((reg) => console.log('Service worker registered', reg))
.catch((err) => console.log('Service worker not registered', err));
}
My service worker looks like this:
const cacheName = 'VSCode_SG_' + Date.now(),
assets = [
'./',
'./app.min.js',
'./favicon.ico',
'./style.css',
];
self.addEventListener('install', evt => {
evt.waitUntil(caches.open(cacheName).then(cache => {
cache.addAll(assets);
}))
});
self.addEventListener('activate', evt => {
// Get all the currently active `Cache` instances.
evt.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheName.includes(key)) {
return caches.delete(key);
}
}));
}));
});
self.addEventListener('fetch', evt => {
evt.respondWith(
caches.match(evt.request).then(cacheRes => {
return cacheRes || fetch(evt.request);
}).catch(() => console.log('An error has occurred'))
);
});
am sure that am missing a small detail to solve the problem but i don't have any clues, anyone could help?
I think your issue might be related to your caches name, you want it to stay a static name instead of labeling it with a date stamp
const cacheName = 'VSCode_SG_STATIC', // keep it a constant string
assets = [
'./',
'./app.min.js',
'./favicon.ico',
'./style.css',
];
Give this a shot and wait until your new service worker is installed to try again
When I try to access http://localhost/visites/ it should fetch the precached file visites/index.php . So I guess I have to indicate somewhere that this particular route matches that file, do you have any idea on how I can do that?
I leave my SW code here just in case:
const cacheName = 'v1';
const cacheAssets = [
'accueil.php',
'js/accueil.js',
'visites/index.php',
'js/visites.js',
'js/global.js',
'css/styles.css',
'charte/PICTOS/BTN-Visites.png',
'charte/STRUCTURE/GESTEL-Logo.png',
'charte/PICTOS/BTN-Animaux.png',
'charte/PICTOS/BTN-Deconnexion.png',
'charte/PICTOS/BTN-Fermes.png',
];
// Call Install Event
self.addEventListener('install', e => {
console.log('Service Worker: Installed');
e.waitUntil(
caches
.open(cacheName)
.then(cache => {
console.log('Service Worker: Caching Files');
cache.addAll(cacheAssets);
})
.then(() => self.skipWaiting())
);
});
// Call Activate Event
self.addEventListener('activate', e => {
console.log('Service Worker: Activated');
// Remove unwanted caches
e.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache => {
if (cache !== cacheName) {
console.log('Service Worker: Clearing Old Cache');
return caches.delete(cache);
}
})
);
})
);
});
// Call Fetch Event
self.addEventListener('fetch', e => {
console.log('Service Worker: Fetching');
e.respondWith(fetch(e.request).catch(() => caches.match(e.request)));
})
You can include some logic in your fetch handler that accounts for this routing information:
self.addEventListener('fetch', e => {
// Use a URL object to simplify checking the path.
const url = new URL(e.request.url);
// Alternatively, check e.request.mode === 'navigate' if
// you want to match a navigation to any URL on your site.
if (url.pathname === '/visites/') {
e.respondWith(caches.match('visites/index.php'));
// Return after responding, so that the existing
// logic doesn't get triggered.
return;
}
e.respondWith(fetch(e.request).catch(() => caches.match(e.request)));
});
I have a ReactJS web with two modes: development and production.
development mode using port: 3001
production using port: 3000
Service worker in development mode is running normally. But, after I run the build process. the result of the output script differs from the development mode.
How to make the service worker script output the same (development mode and production)?
service-worker.js (development mode output script)
const OFFLINE_VERSION = 1;
const CACHE_NAME = 'v-208';
// const OFFLINE_URL = 'offline.html';
const OFFLINE_URL = [
'/offline.html',
'/media/error/bg6.jpg',
'/favicon.ico'
];
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
self.addEventListener('install', (event) => {
event.waitUntil(async function() {
const cache = await caches.open(CACHE_NAME);
await cache.addAll(OFFLINE_URL);
}());
});
self.addEventListener('activate', (event) => {
var cacheWhitelist = [CACHE_NAME];
event.waitUntil(async function() {
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (cacheWhitelist.indexOf(key) === -1) {
return caches.delete(key);
}
}));
})
// Feature-detect
if (self.registration.navigationPreload) {
// Enable navigation preloads!
await self.registration.navigationPreload.enable();
}
}());
});
self.addEventListener('fetch', (event) => {
const { request } = event;
// Always bypass for range requests, due to browser bugs
if (request.headers.has('range')) return;
event.respondWith(async function() {
// Try to get from the cache:
const cachedResponse = await caches.match(request);
if (cachedResponse) return cachedResponse;
try {
// See https://developers.google.com/web/updates/2017/02/navigation-preload#using_the_preloaded_response
const response = await event.preloadResponse;
if (response) return response;
// Otherwise, get from the network
return await fetch(request);
} catch (err) {
// If this was a navigation, show the offline page:
if (request.mode === 'navigate') {
return caches.match('offline.html');
}
// Otherwise throw
throw err;
}
}());
});
build process :
npm run build
then I run it using pm2 :
pm2 serve build --watch --name ekinerja-frontend 3000 --spa
service-worker.js (production mode output script)
importScripts(
"/precache-manifest.4cd8995656b1c33c355715652d9d2264.js"
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
workbox.core.clientsClaim();
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
});
You need to change the path of registering for service worker. After build the whole code will move to a new folder. So it will be suggested that keep your service worker outside src, in the static folder and use this while registering your SW-
if ("serviceWorker" in navigator) { navigator.serviceWorker .register("/static/js/serviceWorker.js") .then(....)}
I have been working on pwa project and i opened two cache
one is static-cache and another one is dynamic-cache i am able to delete one cache at a time by using this code
self.addEventListener('activate', event => {
console.log('Activating new service worker...');
const cacheWhitelist = [staticCacheName];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
i want to delete both caches at same time
Your code looks already good. In my case, I use the following code to delete the SW caches:
if ('caches' in window) {
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.filter(cacheName => {
// You can have some custom logic here, if you want
// to delete only some caches
// If you return TRUE, the cache will be deleted
}).map(cacheName => {
return caches.delete(cacheName);
})
);
})
}
The caches.keys() method returns the keys of the CacheStorage, an interface representing the storage for the Cache objects that can be accessed by the service worker.
I wrote an article about service workers and caching strategies, if you want to deepen the topic.
Not an expert but I think using filter like this will work.
cacheNames.filter(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
try this:
self.addEventListener( "activate", event => {
event.waitUntil(
//wholesale purge of previous version caches
caches.keys().then( cacheNames => {
cacheNames.forEach( value => {
caches.delete( value );
} );
} );
} );