My service worker perform a cache on my offline.html and resources ( jpg, js, css files) for this html page. That works so good for a few hours but after that, only remains cached the offline.html but not the jpg, js, css files.
I wonder if the browser has a limit (time, weigth, etc). the service worker fails? Do I should use cache.put instead cache.addAll?, why is failing?.
//This is the "Offline page" service worker
//Install stage sets up the offline page in the cahche and opens a new cache
self.addEventListener('install', function(event) {
var offlinePage = new Request('offline.html');
event.waitUntil(
fetch(offlinePage).then(function(response) {
return caches.open('offline1').then(function(cache) {
console.log('[off] Cached offline page during Install'+ response.url);
return cache.put(offlinePage, response);
});
}));
});
//If any fetch fails, it will show the offline page.
//Maybe this should be limited to HTML documents?
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function(error) {
console.error( '[off] Network request Failed. Serving offline page ' + error );
return caches.open('offline1').then(function(cache) {
return cache.match('offline.html');
});
}));
});
//cache of files that are part of offline.html cache.put
var ASSETS = [
'/pwa/offline/js/main.js',
'/upload/off-1about.jpg',
'/upload/off-1best-bg.jpg',
'/upload/off-1gallery-bg.jpg',
'/upload/off-1intro-bg.jpg',
'/upload/off-1logo.png',
'/upload/off-1menu-bg.jpg',
'/upload/off-best-01.jpg',
'/upload/off-best-02.jpg',
'/upload/off-best-03.jpg',
'/upload/off-call-me-32.png'
];
self.oninstall = function (evt) {
evt.waitUntil(caches.open('offline1').then(function (cache) {
return Promise.all(ASSETS.map(function (url) {
return fetch(url).then(function (response) {
return cache.put(url, response);
});
}));
}))
};
// or
//cache of files that are part of offline.html cache.addAll
/*
var ASSETS = [
'/pwa/offline/js/main.js',
'/upload/off-1about.jpg',
'/upload/off-1best-bg.jpg',
'/upload/off-1gallery-bg.jpg',
'/upload/off-1intro-bg.jpg',
'/upload/off-1logo.png',
'/upload/off-1menu-bg.jpg',
'/upload/off-best-01.jpg',
'/upload/off-best-02.jpg',
'/upload/off-best-03.jpg',
'/upload/off-call-me-32.png'
];
self.oninstall = function (evt) {
evt.waitUntil(caches.open('offline-cache-name').then(function (cache) {
return cache.addAll(ASSETS);
}))
};
*/
//This is a event that can be fired from your page to tell the SW to update the offline page
self.addEventListener('refreshOffline', function(response) {
return caches.open('offline1').then(function(cache) {
console.log('[off] Offline page updated from refreshOffline event: '+ response.url);
return cache.put(offlinePage, response);
});
});
Related
I'm trying to implement a basic service worker to assure that users of my simple web app have the latest code. So when I update html, js, or css files I can increment the cachename in the service worker file and prompt users to refresh, clear their cache, and get the latest code.
Until now I've relied on hacky ways to update javascript files (including a parameter in the referring URL: /javascript-file.js?v=1).
The with the service worker code below seem unpredictable: sometimes small changes to JS or CSS are reflected after I increment the cachename (code below). Sometimes the changes are reflected without incrementing the cachename, which suggests the code is ALWAYS pulling from the network (wasting resources).
How can you troubleshoot which version of files the code is using and whether the service worker is using cached or network versions? Am I not understanding the basic model for using service workers to achieve this goal?
Any help appreciated.
serv-worker.js (in root):
console.log('Start serv-worker.js');
const cacheName = '3.2121';
var urlsToCache = [
'home.html',
'home-js.js',
'web-bg.js',
'css/main.css',
'css/edit-menus.css'
];
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);
})
);
});
// Network first.
self.addEventListener('fetch', (event) => {
// Check the cache first
// If it's not found, send the request to the network
// event.respondWith(
// caches.match(event.request).then(function (response) {
// return response || fetch(event.request).then(function (response) {
// return response;
// });
// })
// );
event.respondWith(async function() {
try {
console.log('aPull from network...', event.request);
return await fetch(event.request);
} catch (err) {
console.log('aPull from cache...', event.request);
return caches.match(event.request);
}
}());
});
self.addEventListener('message', function (event) {
console.log('ServiceWorker cache version: ', cacheName, event);
console.log('Received msg1: ', event.data);
if (event.data.action === 'skipWaiting') {
console.log('ccClearing cache: ', cacheName);
// caches.delete('1.9rt1'); // hardcode old one
// caches.delete(cacheName); // actually removes cached versions
caches.keys().then(function(names) {
for (let name of names)
caches.delete(name);
});
self.skipWaiting();
}
});
Code in web-bg.js, which home.html references:
function servWorker(){
let newWorker;
function showUpdateBar() {
console.log('Show the update mssgg...ddddd');
$('#flexModalHeader').html('AP just got better!');
$('#flexModalMsg').html("<p>AP just got better. Learn about <a href='https://11trees.com/support/release-notes-annotate-pro-web-editor/'>what changed</a>.<br><br>Hit Continue to refresh.</p>");
$('#flexModalBtn').html("<span id='updateAPbtn'>Continue</span>");
$('#flexModal').modal('show');
}
// The click event on the pop up notification
$(document).on('click', '#updateAPbtn', function (e) {
console.log('Clicked btn to refresh...');
newWorker.postMessage({ action: 'skipWaiting' });
});
if ('serviceWorker' in navigator) {
console.log('ServiceWORKER 1234');
navigator.serviceWorker.register(baseDomain + 'serv-worker.js').then(reg => {
console.log('In serviceWorker check...', reg);
reg.addEventListener('updatefound', () => {
console.log('A wild service worker has appeared in reg.installing!');
newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
// Has network.state changed?
console.log('SSState is now: ', newWorker.state);
switch (newWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// new update available
console.log('Detected service worker update...show update...');
showUpdateBar();
}
// No update available
break;
}
});
});
});
let refreshing;
navigator.serviceWorker.addEventListener('controllerchange', function (e) {
console.log('a1111xxxListen for controllerchange...', e);''
if (refreshing) return;
console.log('Refresh the page...');
window.location.reload();
refreshing = true;
});
} // End serviceworker registration logic
return;
} // END serv-worker
You've commented out the section for /// Check the cache first and then below that the try/catch statement again pulls from the network and falls back to the cache.
Uncomment this section of code and see if you're loading from the cache first.
// event.respondWith(
// caches.match(event.request).then(function (response) {
// return response || fetch(event.request).then(function (response) {
// return response;
// });
// })
// );
Don't forget that even if you request from the network from the service worker the browser will still use it's own internal cache to serve data. How long the data stays in the browser's cache depends on the expiration headers being sent by the server.
When using expires, it's still a fairly common solution to do something like:
index.html - expires after an hour. Has script/css tags that call out file names with ?v=x.y.z
/resources - folder that holds js and css. This folder has a very long expiration time. But that long expiration is short circuited by changing the ?v=x.y.z in index.html
I've used the above successfully in Progressive Web Apps (PWAs). But it is a little painful when debugging. The best option here is to manually clear out the cache and service worker from Dev Tools \ Application, if you're in Chrome.
I am developing a PWA app. Although I am a beginner in JavaScript and HTML, I was able to almost complete the coding. But I have an issue with caching.
The app is quite simple: in a single page there are some images that are used to display PDF files. When no network is present, it should work locally from the cache. The app will be used on an Android tablet. I ma testing on Windows 10 using Chrome. That was working fine. During the tests, if a PDF file was changed on the server, and then the network was back I needed to empty the cache for the app to show the new PDF file. I modified the design to use the cache then network method, but that does not work. I just can't figure out what's wrong. Here is the actual code.
/sw.js
const staticAssets = [
'/',
'/index.html',
'/sw.js',
'css/bootstrap.min.css',
'js/app.js',
'js/bootstrap.min.js',
'js/jquery.min.js',
'js/jquery.slim.min.js',
'js/popper.min.js',
'/icons/generic_128.png',
'icons/generic_512.png',
'images/Ashkeepers.png',
'images/Canvases.png',
'images/Forfaits thématiques.png',
'images/Funeral urn.png',
'images/logo fictif.png',
'images/Memorial products.png',
'images/Produits commémoratifs.png',
'images/Reliquaires.png',
'images/Thematic packages.png',
'images/Toiles.png',
'images/Urnes.png',
'pdf/02_urnes_funeraires_02_ENG_HR.pdf',
'pdf/02_urnes_funeraires_04_HR.pdf',
'pdf/03_toiles_03_ENG_HR.pdf',
'pdf/03_toiles_04_HR.pdf',
'pdf/04_forfaits_thematiques_05_ENG_HR.pdf',
'pdf/04_forfaits_thematiques_12_HR.pdf',
'pdf/05_reliquaires_03_ENG_HR.pdf',
'pdf/05_reliquaires_04_HR.pdf',
'pdf/06_produits_funeraires_02_ENG_HR.pdf',
'pdf/06_produits_funeraires_03_HR.pdf'
];
self.addEventListener('install', function (event) {
console.log('App Installed');
event.waitUntil(
caches.open('static')
.then(function (cache) {
cache.addAll(staticAssets);
})
);
});
self.addEventListener('activate', function () {
console.log('App Activated');
});
/*
Version originale
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(res) {
if (res) {
console.log("From cache");
return res;
} else {
console.log("From Internet");
return fetch(event.request);
}
})
);
});
*/
/* Cache the network */
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('static').then(function(cache) {
return fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
console.log('Data en cache: ' + response.clone()); /* AJOUTÉ */
return response;
});
})
);
});
/js/App.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function() {
console.log('SW registered');
});
}
/js/feed.js
var networkDataReceived = false;
// fetch fresh data
var networkUpdate = fetch('/data.json').then(function(response) {
return response.json();
}).then(function(data) {
networkDataReceived = true;
updatePage(data);
}).catch(function() {
/* Il faut attraper l'exception si data.json est introuvable */
});
// fetch cached data
caches.match('/data.json')
.then(function(response) {
if (!response) throw Error("No data");
return response.json();
}).then(function(data) {
// don't overwrite newer network data
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
// we didn't get cached data, the network is our last hope:
return networkUpdate;
});
Can someone bring some light to my poor brain ?
Thanks a lot
EDITED EDITED EDITED
I forgot to tell how I display PDF files:
<a href="pdf/03_toiles_03_ENG_HR.pdf" class="mx-auto d-block">
Can that explains the issues I have ? I know that a service worker scope is specific, and I wonder if it could be that the SW don't work on the 'page' that's used to display the PDF.
I'm studying service worker for my app.
This service worker cookbook(https://serviceworke.rs) is very helpful for me.
I'm configuring "Cache, update and refresh" strategy but can not know that how to add webpack hash name to service worker file.
For this, there is a few option, I know,
Workbox (https://github.com/GoogleChrome/workbox)
sw-precache (https://github.com/GoogleChromeLabs/sw-precache)
sw-precache-webpack-plugin (https://github.com/goldhand/sw-precache-webpack-plugin#readme)
but I just want to make service worker file without plugin or 3rd tools AND put webpack chunkhash file name into my service worker file.
my-sw.js
var CACHE = 'cache-update-and-refresh';
self.addEventListener('install', function(evt) {
console.log('The service worker is being installed.');
evt.waitUntil(caches.open(CACHE).then(function (cache) {
cache.addAll([
'./controlled.html',
'./asset',
// here is problem. how to put this?
/**
main.72b835f75acd535f504c.js,
vendor.d6b44fd0b72071c85ce2.js,
aboutUs.d31139a7f363d5254560.js,
...etc...
**/
]);
}));
});
self.addEventListener('fetch', function(evt) {
console.log('The service worker is serving the asset.');
evt.respondWith(fromCache(evt.request));
evt.waitUntil(
update(evt.request)
.then(refresh)
);
});
function fromCache(request) {
return caches.open(CACHE).then(function (cache) {
return cache.match(request);
});
}
function update(request) {
return caches.open(CACHE).then(function (cache) {
return fetch(request).then(function (response) {
return cache.put(request, response.clone()).then(function () {
return response;
});
});
});
}
// Sends a message to the clients.
function refresh(response) {
return self.clients.matchAll().then(function (clients) {
clients.forEach(function (client) {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message));
});
});
}
This is my first PWA app with laravel. This code is working,it gets registered well, but if I do a change in the code, for example in the HTML, it is not getting update, and the console is not throwing errors, and I dont know why.
I'm using this code to call the service-worker.js
if ('serviceWorker' in navigator ) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
And this is the code of the sw.js
var cache_name = 'SW_CACHE';
var urlsToCache = [
'/',
'/register'
];
self.addEventListener('install', function(event) {
event.waitUntil(precache());
});
addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
var responseToCache = response.clone();
caches.open(cache_name)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
var fromCache = function (request) {
return caches.open(cache_name).then(function (cache) {
cache.match(request).then(function (matching) {
return matching || Promise.resolve('no-match');
});
});
}
var update = function (request) {
return caches.open(cache_name).then(function (cache) {
return fetch(request).then(function (response) {
return cache.put(request, response);
});
});
}
var precache = function() {
return caches.open(cache_name).then(function (cache) {
return cache.addAll(urlsToCache);
});
}
Y also used skipWaiting(); method inner Install method, but it crash my app and have to unload the sw from chrome://serviceworker-internals/
This is what service worker lifecycle suppose to work: a new service worker won't take place, unless:
The window or tabs controlled by the older service worker are closed and reopened
'Update on reload' option is checked in Chrome devtools
Here is an official tutorial explained it well: The Service Worker Lifecycle
Service worker will always use the existing worker. Two thinks you can do is in chrome there is an option to set update on load
Goto InspectorWindow (f12) -> application -> and check update on reload.
if you want immediate update you can choose the network first cache approach. which will take the latest from server always and use the cache only in offline mode. see the link for more information
How API is getting cached effectively, Using Service worker in Angular 5 using Angular CLI
I have a nginx hosted Jekyll site.
I have made several changes to my site, update package version and changed my javascript to update the service worker. However my changes are still not reflecting in chrome;
main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', {scope: 'sw-test'}).then(function(registration) {
// registration worked
console.log('Registration succeeded.');
registration.update();
}).catch(function(error) {
// registration failed
console.log('Registration failed with ' + error)
});
}
sw.js
var PRECACHE = 'precache-{{site.version}}';
var RUNTIME = 'runtime';
// A list of local resources we always want to be cached.
var PRECACHE_URLS = [
'./',
'/index.html',
'/assets/css/main.css',
'/assets/js/vendor/modernizr-custom.js',
'/assets/js/bundle.js'
];
// 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 => {
var 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;
});
});
});
})
);
}
});
Chrome Screenshot
Screenshot
Any help greatly appreciated!!
Your nginx config is most likely setting some caching headers on the SW script. When that's the case, the browser returns the old version from its local cache skipping network completely.