先记住这个答案
SharedWorker 构造函数在首次调用时创建脚本实例,后续同源调用返回同一个实例并分配新的 MessagePort。每个页面通过该端口与 worker 双向通信,两者必须显式调用 start() 或设置 onmessage 才能激活。worker 内部通过 connect 事件的 ports[0] 获取当前连接,并需自行保存端口列表以定向回复。只要还有页面持有 worker 引用,该 worker 就不会终止。
- 同源页面才能共享 Worker
- 每个页面一个独立 port
- 活引用决定 Worker 存活
连接建立与消息分发机制
当页面执行 new SharedWorker(url) 时,浏览器先按 url 和同源策略检查是否已有同脚本 Worker。若不存在则加载并创建 SharedWorkerGlobalScope,若已有则直接复用。无论哪种情况,构造函数都返回一个包含 port 属性的 SharedWorker 对象,该 port 是新生成的 MessagePort,代表页面与 Worker 的通信通道,注意此端口默认未激活。
激活端口有两个等价方法:设置 port.onmessage 或调用 port.start()。在 Worker 内部,每次页面连接都会触发 connect 事件,事件对象的 ports 数组只含一个新 MessagePort,这是当前页面专属的端口。Worker 必须保存这些端口才能向特定页面回复,并通过监听 message 事件获取数据。同源策略始终贯穿——跨源页面无法获得同一 Worker 实例,因为查找 key 就是脚本 URL 与源。
多标签页协同编辑状态同步
假设一个在线文档编辑器,用户同时打开三个同源标签页编辑同一文档。使用 SharedWorker 存储文档最新全文,并对所有标签页广播变更。每个页面创建 SharedWorker 后记录 port,Worker 维护一个 Set 保存所有连接 port。当某页修改内容后,向 Worker 发消息,Worker 更新内部文本并遍历 Set 向每个 port 发送最新全文。这样所有页面立刻同步,且因为状态集中于 Worker,无需跨页面协调即可保证一致性。
实施时需处理端口断开:当标签页关闭,该 port 将不再可用,但 Worker 无法直接获知,因为 MessagePort 没有关闭事件。因此需要监听消息错误或定时发送心跳探测(例如每 10 秒检查是否收到 pong),失败则从 Set 删除。最后,当所有标签页都关闭,最后一个引用消失后 Worker 自动终止,不需要显式析构,但这意味着未持久化的状态会丢失,若需要持久化可在 beforeunload 中把状态写入 localStorage 或 indexDB。
适用范围与失效条件
SharedWorker 只在完全同源(协议、域名、端口一致)的环境中有效,跨源页面无法创建或连接到同一 Worker,因此跨源场景需使用 postMessage 等跨文档消息机制。此外 SharedWorker 不能从专用 Worker 内部创建,因为其构造函数只在 Window 和 SharedWorkerGlobalScope 中暴露。支持程度也参差不齐,特别是移动 Safari 直到 2026 年才启用,部署前需用 typeof SharedWorker 检测。
生命周期受引用计数控制,默认在最后一个页面关闭后立即终止。若需要保留短暂时间完成清理,可传入选项 extendedLifetime: true,但兼容性有限。另外,每次连接产生一个新端口,Worker 必须管理端口集合,若处理不当会发生端口泄漏,表现为连接数持续增长、消息广播给已离开页面等。必须结合 connect 事件和消息错误检测(例如监听 messageerror 或心跳)及时清理,代价是额外的心跳或 ref-count 代码。
容易答错的地方
- SharedWorker 可以跨域共享
- 这是错误理解。SharedWorker 实例由脚本 URL 和源共同键控,不同协议、域名或端口的页面无法访问同一 Worker,只有同源页面才能连接。
- 忘记 port.start() 导致收不到消息
- 如果使用 addEventListener 监听而没有调用 start(),消息不会自动传递。正确做法是调用 start() 或直接用 onmessage 赋唤隐式启动。
面试官还会怎么问?
如何在 Worker 内广播给所有连接端口?
Worker 在 connect 事件中拿到每个端口并保存到数组或 Set,广播时遍历所有端口调用 postMessage。需注意新端口加入和旧端口移除,避免发送到已关闭端口。
SharedWorker 与 BroadcastChannel 有何区别?
SharedWorker 是独立线程,能保存状态、执行复杂逻辑、维持有序消息;BroadcastChannel 只做轻量广播,不提供状态和管理能力,但兼容性更广。
同一客户端的多个消息到达顺序是否一定?
每个 MessagePort 通道内的消息按发送顺序到达,但不同端口之间没有顺序保证。若要跨页面严格排序,需在 Worker 中统一处理。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。