I'm using the default file in a cli-vue 3 new app. What I need is to reload a page when service worker detects and updates new files.
Using your default hook I tried to use this code:
js updated (registration) {
console.log('New content is available; please refresh.')
window.location.reload() },
It works but it reloads forever!
Why?
I read a lot about Google Workbox and @vue/cli-plugin-pwa and I know these default hooks are bundled in final .js and executed on the main thread. So I think maybe I'm wrong? I used create-react-app default service-worker.js and the reload used to work.
Where am I wrong?
It works but it reloads forever!
Could you please share a demonstration repository?
Clopsing for inactivity.
window.location.reload() needs to be changed to window.location.reload(true), otherwise it will just reload from cache.
@LinusBorg why isn't it the default behaviour to load the latest content when it's detected? is there a way to make it the default behaviour or is editing the service-worker and adding a reload the proper way to do it?
@Ol1BoT I tried adding window.location.reload(true) as you suggested but it still reloaded from the cache and entered an infinite reload loop
Having the same problem. I'm stuck on a reload loop after doing a window.location.reload(true).
I have to manually CMD + SHIFT + R to get out of that loop.


Me too.
But unfortunately, it's hard to reproduce, sometimes it's ok, sometimes it's not. With the same code, only some user has this problem.
Also, related issue in register-service-worker:
https://github.com/yyx990803/register-service-worker/issues/14
@Ol1BoT I tried adding
window.location.reload(true)as you suggested but it still reloaded from the cache and entered an infinite reload loop
Yeah, what I suggested didn't work as intended. I can't remember exactly how I got around this issue, but I changed my hosting caching options, which forces the application to update to the new version as it detected the changes.
I just ended up switching to the vue-cli v4 alpha really early since I really wanted the new pwa workbox options. I am now able to properly detect changes and prompt the user to reload the app properly. Although it seems like this prompt only occurs when a user has reloaded the page or closed/opened the pwa. Maybe I'm wrong, I would hope that the new changes could be detected even during a long session
in case it helps anyone, here's my whole setup
vue.config.js
module.exports = {
...
pwa: {
name: 'redacted',
themeColor: '#d70000',
msTileColor: '#333333',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'default',
assetsVersion: '',
manifestPath: 'manifest.json',
manifestOptions: {
name: 'redacted',
short_name: 'redacted',
description: 'redacted',
lang: 'fr-CA',
start_url: '.',
background_color: '#333333',
theme_color: '#d70000',
display: 'standalone',
orientation: 'portrait',
},
iconPaths: {
favicon32: 'img/icons/favicon-32x32.png',
favicon16: 'img/icons/favicon-16x16.png',
appleTouchIcon: 'img/icons/apple-icon.png',
maskIcon: 'img/icons/safari-pinned-tab.svg',
msTileImage: 'img/icons/ms-icon-144x144.png',
},
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// skipWaiting: true,
swSrc: 'src/service-worker.js',
exclude: [
/\.map$/,
],
},
},
}
registerServiceWorker.js
/* eslint-disable no-console */
import { register } from 'register-service-worker';
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready() {
console.log(
'App is being served from cache by a service worker.\n'
+ 'For more details, visit https://goo.gl/AFskqB',
);
},
// registered() {
// console.log('Service worker has been registered.');
// },
cached() {
console.log('Content has been cached for offline use.');
},
// updatefound() {
// console.log('New content is downloading.');
// },
updated(registration) {
console.log('New content is available! We\'ll show a refresh button for the user to click on and refresh');
document.dispatchEvent(new CustomEvent('swUpdated', { detail: registration }));
},
offline() {
console.log('No internet connection found. App is running in offline mode.');
},
error(error) {
console.error('Error during service worker registration:', error);
},
});
}
service-worker.js
if (workbox) {
workbox.precaching.precacheAndRoute(self.__precacheManifest);
}
// This code listens for the user's confirmation to update the app.
console.log('sw:listening for message events');
self.addEventListener('message', (e) => {
console.log('sw:message received', e);
if (!e.data) {
return;
}
switch (e.data) {
case 'skipWaiting':
console.log('skipWaiting');
self.skipWaiting();
break;
default:
// NOOP
break;
}
});
Update.vue
<template>
<div class="text-center update-container">
<h1>An update is available</h1>
<button
class="btn btn-default"
@click="refreshApp"
>
<i class="zmdi zmdi-refresh" /> Click to update!
</button>
</div>
</template>
<script>
export default {
methods: {
refreshApp() {
Bus.$emit('refresh_app');
},
},
};
</script>
<style>
.update-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#notification {
position: relative;
width: 280px;
font-size: 12px;
padding: 0px 24px 24px;
box-sizing: border-box;
margin-left: auto;
margin-right: auto;
}
#notification p {
margin: 0;
}
#notification a {
color: #42b983;
}
#notification button {
position: absolute;
top: 0px;
right: 0px;
padding: 0 5px 2px;
background: #1da025;
color: #fff;
font-size: 13px;
}
#notification button:hover {
cursor: pointer;
}
</style>
update.js (mixin)
export default {
data() {
return {
// refresh variables
refreshing: false,
registration: null,
updateExists: false,
};
},
created() {
// ---
// Custom code to let user update the app
// when a new service worker is available
// ---
document.addEventListener('swUpdated', this.showRefreshUI, { once: true });
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (this.refreshing) return;
this.refreshing = true;
// Here the actual reload of the page occurs
window.location.reload();
});
Bus.$on('refresh_app', () => {
this.refreshApp();
});
},
methods: {
showRefreshUI(e) {
this.registration = e.detail;
this.updateExists = true;
},
refreshApp() {
this.updateExists = false;
if (!this.registration || !this.registration.waiting) return;
// send message to SW to skip the waiting and activate the new SW
this.registration.waiting.postMessage('skipWaiting');
},
},
beforeDestroy() {
Bus.$off('update_app');
},
};
You don't need to use a custom service worker just to fire skipWaiting anymore. If you look at the default generated worker under dist/service-worker.js, you will see:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
You can accomplish the same thing as @vesper8 with the default worker with:
this.registration.waiting.postMessage({ type: 'SKIP_WAITING' });
instead of this.registration.waiting.postMessage('skipWaiting');
Most helpful comment
in case it helps anyone, here's my whole setup
vue.config.js
registerServiceWorker.js
service-worker.js
Update.vue
update.js (mixin)