Service Worker 与主线程通信基于 postMessage 异步机制,主线程可通过 navigator.serviceWorker.controller(页面已被控制时)或 registration.active(SW 处于 active 状态时)发送消息,并需提前在对应对象上监听 message 事件接收响应;SW 必须通过 event.source.postMessage 回复,且消息内容需为可结构化克隆的对象。

Service Worker 与主线程通信本质是基于 postMessage 的异步消息机制,不是实时连接,而是“发—收—回”的事件驱动模型。关键在于明确谁发、谁收、何时可发,以及如何确保消息不丢失。
主线程向 Service Worker 发消息的两种可靠方式
注册成功后,主线程可通过以下任一途径发送消息,但适用场景不同:
-
用
navigator.serviceWorker.controller:仅当页面已被该 SW 控制时有效(比如刷新后)。适合已稳定运行的场景,例如同步用户状态、触发缓存更新。 -
用
registration.active:只要 SW 处于 active 状态就可用,哪怕还没接管当前页面(比如首次安装完成瞬间)。适合注册后立刻下发初始化指令。
⚠️ 注意:如果 SW 还在 installing 或 waiting 阶段,active 可能为 null。此时应监听 statechange 事件,或使用 navigator.serviceWorker.ready Promise 确保 readiness。
Service Worker 接收并响应消息的正确写法
在 SW 脚本(如 sw.js)中,不能直接调用 self.postMessage() 回复——它没有这个方法。必须通过事件对象的 source 属性把消息送回去:
self.onmessage = ({ data, source }) => {
console.log("收到主线程消息:", data);
// ✅ 正确:用 source.postMessage 回复
if (source) {
source.postMessage(`已处理:${data}`);
}
};
这样主线程就能在 navigator.serviceWorker.controller 或 worker 实例上监听 message 事件接收回应。
主线程监听 Service Worker 的回复
主线程需提前绑定监听器,且推荐在注册后立即设置,避免错过响应:
- 若通过
controller发送,就在controller上监听:navigator.serviceWorker.controller.addEventListener("message", e => {...}) - 若通过
registration.active发送,就在该 worker 实例上监听:registration.active.addEventListener("message", e => {...})
注意:监听器必须在发送前注册,否则可能收不到第一次响应。
常见陷阱与规避建议
实际开发中最容易出问题的地方集中在时机和生命周期上:
- 页面首次加载时
controller为null,误用会导致postMessage报错;应改用registration.active或等待ready。 - SW 被 terminate 或更新后,旧的
active实例可能失效;建议每次通信前检查worker.state === "activated"。 - 消息内容必须可结构化克隆(不能含函数、DOM 节点等),否则会静默失败;建议统一用 plain object + JSON 安全字段。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











