You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 21:23:11