cache index.html for offline, but refresh on new deploy
I am currently using the sw-precache-webpack-plugin with this config: (yes, another project, this is just to show the config, since some of the configuration options are passed to sw-precache + sw-toolbox). This is a SPA.
new SWPrecacheWebpackPlugin({
cacheId: 'project',
filename: 'project-sw.js',
minify: true,
maximumFileSizeToCacheInBytes: 3500000,
filepath: path.join(assetsDir, '/project-sw.js'),
staticFileGlobs: ['dist/assets/index.html'],
mergeStaticsConfig: true,
runtimeCaching: [
{
urlPattern: /\/api\//,
handler: 'networkFirst'
}
],
staticFileGlobsIgnorePatterns: [/project-sw\.js$/i],
stripPrefix: 'dist/assets/',
navigateFallback: '/index.html',
navigateFallbackWhitelist: [/^((?!adomain|anotherdomain).)*$/]
})
We initialize the service worker like this in an attempt to always load the new service worker when we deploy:
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/project-sw.js?build=' + __webpack_hash__);
}
Operation-wise, it works as intended. But, we cache index.html for offline usage, and this also caches the reference for the app file, app.<__webpack_hash__>.js, leading to the wrong bundles being requested.
Scenario (from clean slate):
- Open
/index.html app.fd9e432.jsis loaded- service worker is loaded with
/project-sw.js?build=fd9e432
.. next visit after redeploy (new hash):
- Open
/index.html(which is cached by the previous service worker) app.fd9e432.jsis loaded (same as last time)- The correct service worker is loaded with
/project-sw.js?build=fd9e432 - Goes to
/newpath, webpack tries to load bundle with old hash:newpath.fd9e432.jsand gets a 404
The cache headers on index.html and project-sw.js is the same (no cache):
cache-control: no-store, no-cache, must-revalidate, proxy-revalidate
etag: W/"241e-6NP8wbah/2UUxPQ4NPT2uJkYld8"
expires: 0
pragma: no-cache
Is there a configuration I am missing here, or what am I doing wrong?
Hm 🦆 , is it perhaps possible to avoid the issue by not using hashes in the bundles? That app.<hash>.js -> app.js et al? Then the reference would be sane (we're not caching static files on the server but in the service worker)?
Update, the issue has a fix, but I don't think the underlying issue is resolved. Here is how we force a reload of the service worker when new content is detected: (It's a workaround at best)
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/project-sw.js?build=' + __webpack_hash__)
.then(function(reg) {
// registration worked
console.info('Service Worker Registration succeeded. Scope is ' + reg.scope);
// updatefound is fired if service-worker.js changes.
reg.onupdatefound = function() {
// The updatefound event implies that reg.installing is set; see
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
var installingWorker = reg.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and the fresh content will
// have been added to the cache.
// It's the perfect time to display a "New content is available; please refresh."
// message in the page's interface.
// This is also a nice place to put major/minor version logic to inform the user of new features
console.info('New or updated content is available.');
window.location.reload(true);
} else {
// At this point, everything has been precached.
// It's the perfect time to display a "Content is cached for offline use." message.
console.info('Content is now available offline!');
}
break;
case 'redundant':
console.error('The installing service worker became redundant.');
break;
}
};
};
})
.catch(function(error) {
// registration failed
console.error('Service Worker Registration failed with ' + error);
});
}