如何让PWA的Service Worker在页面加载完成后发送加载状态消息?
问题:如何让Service Worker在页面就绪后发送加载状态消息
我有一个运行良好的渐进式Web应用(PWA)网站,需要在页面上显示当前内容是从网络加载还是从缓存读取。现有Service Worker的fetch事件代码可正常从网络获取内容并缓存,离线时从缓存读取,但修改响应体以展示状态过于复杂。尝试使用BroadcastChannel让Service Worker向页面发送状态消息,但当前代码无法正常工作,推测是因为Worker在页面就绪前就发送了消息。需要实现让Service Worker仅在页面加载完成后发送消息。
现有代码
Service Worker 核心fetch代码
self.addEventListener("fetch", event => { event.respondWith((async () => { const cache = await caches.open(CACHE_NAME); try{ const fetchResponse = await fetch(event.request); cache.put(event.request,fetchResponse.clone()); //modify response body here, appending "ONLINE VERSION" to the end of the returned HTML return fetchResponse; } catch(e) { const cachedResponse = await cache.match(event.request); if (cachedResponse) { //modify response body here, appending "OFFLINE VERSION" to the end of the returned HTML return cachedResponse; } } })());
尝试添加的Service Worker消息代码
const broadcast = new BroadcastChannel('xxx'); broadcast.onmessage = (event) => { if (event.data && event.data.type === 'ccc') { broadcast.postMessage({ "aaa":"bbb" }); } };
页面端代码
const broadcast = new BroadcastChannel('xxx'); // Listen to the response broadcast.onmessage = (event) => { console.log(event.data.payload); };
解决方案
核心思路是让页面先向Service Worker发送"就绪"信号,Service Worker收到信号后再发送加载状态;同时暂存页面就绪前的fetch状态,等信号到达后补发。
1. 修改Service Worker代码
const CACHE_NAME = 'your-cache-name'; // 确保此处已定义缓存名称 const broadcast = new BroadcastChannel('content-status-channel'); let pendingStatus = null; // 暂存页面就绪前的加载状态 let pageReady = false; // 标记页面是否就绪 // 监听页面发送的就绪信号 broadcast.onmessage = (event) => { if (event.data?.type === 'PAGE_READY') { pageReady = true; // 若有暂存状态,立即发送给页面 if (pendingStatus) { broadcast.postMessage({ type: 'CONTENT_STATUS', payload: pendingStatus }); pendingStatus = null; } } }; self.addEventListener("fetch", event => { event.respondWith((async () => { const cache = await caches.open(CACHE_NAME); let status = ''; try { const fetchResponse = await fetch(event.request); cache.put(event.request, fetchResponse.clone()); status = 'ONLINE VERSION'; // 页面就绪直接发消息,否则暂存状态 pageReady ? broadcast.postMessage({ type: 'CONTENT_STATUS', payload: status }) : pendingStatus = status; return fetchResponse; } catch(e) { const cachedResponse = await cache.match(event.request); if (cachedResponse) { status = 'OFFLINE VERSION'; // 页面就绪直接发消息,否则暂存状态 pageReady ? broadcast.postMessage({ type: 'CONTENT_STATUS', payload: status }) : pendingStatus = status; return cachedResponse; } // 无缓存无网络时的兜底响应 return new Response('无法加载内容', { status: 503 }); } })()); });
2. 修改页面端代码
const broadcast = new BroadcastChannel('content-status-channel'); // 页面DOM加载完成后,向Service Worker发送就绪信号 document.addEventListener('DOMContentLoaded', () => { broadcast.postMessage({ type: 'PAGE_READY' }); }); // 接收并处理Service Worker发送的状态消息 broadcast.onmessage = (event) => { if (event.data?.type === 'CONTENT_STATUS') { console.log('当前内容状态:', event.data.payload); // 将状态展示到页面上(示例:更新指定DOM元素) const statusElement = document.getElementById('content-status'); if (statusElement) { statusElement.textContent = `当前加载:${event.data.payload}`; } } };
3. 页面添加状态展示元素(可选)
在HTML中添加一个用于显示状态的元素:
<div id="content-status" style="position: fixed; bottom: 10px; right: 10px; padding: 8px; background: #eee; border-radius: 4px;"></div>
内容的提问来源于stack exchange,提问作者diegosalamares
相关产品推荐
相关产品推荐

