当有多个工作框广播更新消息时仅提示一次



我使用Workbox和BroadcastUpdatePlugin()在我的serviceWorker中提示用户在缓存文件更新时刷新页面。当只有一个文件更新时,它工作得很好,但是当我一次发布多个更新(HTML, CSS和JS文件)时,提示用户为每个文件刷新页面。

如何更新缓存中的所有文件,然后在事件侦听器停止接收更新消息时提示用户仅刷新一次页面?

<ServiceWorker代码/strong>

const {BroadcastUpdatePlugin} = workbox.broadcastUpdate;
registerRoute(
({request}) => request.destination === 'document',
new StaleWhileRevalidate({
//new NetworkOnly({
cacheName: 'pages',
plugins: [
new BroadcastUpdatePlugin(),
],
})
)
registerRoute(
({request}) => request.destination === 'script' || request.destination === 'style',
new StaleWhileRevalidate({
//new NetworkOnly({
cacheName: 'assets',
plugins: [
new BroadcastUpdatePlugin(),
],
})
)

JavaScript代码

if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js', { scope: '/' }).then(swReg => {
console.log('Service Worker Registered', swReg);
}).catch(error => {
console.log('There was an error!', error);
})
})

// Listen for cache updates and prompt a page reload
navigator.serviceWorker.addEventListener('message', async (event) => {
if (event.data.meta === 'workbox-broadcast-update') {
const {cacheName, updatedURL} = event.data.payload;
const cache = await caches.open(cacheName);
const updatedResponse = await cache.match(updatedURL);
const updatedText = await updatedResponse.text();
console.log('Updated: '+cacheName+', '+updatedURL);
// prompts for every update
if(confirm('Content Updated. Please refresh the page.')){
window.location.reload;
}
}
})
}

我可以想象在你的service worker中可能存在可以帮助解决这个问题的逻辑,例如,通过查看触发更新的FetchEvent中的cliendId,并且只有在它之前没有看到clientId时才有条件地发送消息。

但是这将需要一堆自定义逻辑超出BroadcastUpdatePlugin已经提供的,它可能是容易出错的-如果你的服务工作者广播一个更新,然后用户对更新采取行动,然后有另一个更新可以被广播,你可能想给用户另一个机会对它采取行动,但它将很难知道是否广播消息到相同的clientId在那个场景。

一种更简洁的方法是将阻止多个提示的逻辑移到window客户端代码中。

有几种方法可以达到这个目的,但对我来说最干净的方法是依赖于promise的解析来触发提示。你可以多次调用承诺的resolve函数,但承诺链中的then()只会被调用一次,给你你正在寻找的行为。

if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
const waitForUpdate = new Promise((resolve) => {
navigator.serviceWorker.addEventListener('message', async (event) => {
if (event.data.meta === 'workbox-broadcast-update') {
const {cacheName, updatedURL} = event.data.payload;
const cache = await caches.open(cacheName);
const updatedResponse = await cache.match(updatedURL);
const updatedText = await updatedResponse.text();
console.log(`Updated ${updatedURL} in ${cacheName}: ${updatedText}`);
// Calling resolve() will trigger the promise's then() once.
resolve();
}
});
});
waitForUpdate.then(() => {
if (confirm('Content updated. Please refresh the page.')) {
window.location.reload();
}
});
}

(您也可以使用依赖于全局变量或类似的方法,它在您第一次显示提示时被翻转,并在每次后续显示提示时短路,但我喜欢使用承诺来处理这类事情。)

最新更新