sharedworker 通过“一个线程、多个端口、同源共用”实现多标签页状态同步,依赖浏览器原生单实例机制;同源且脚本url完全一致才共享实例;顶层变量内存共享但需防竞态;通信仅限messageport结构化克隆;生命周期独立于页面,状态需持久化到indexeddb或服务端。

SharedWorker 在多标签页间同步状态,靠的是“一个线程、多个端口、同源共用”这一底层机制——它不是靠轮询或广播模拟,而是浏览器原生保证的单实例运行模型。
同源唯一实例是同步前提
只要页面同源(协议+域名+端口相同),且加载的 SharedWorker 脚本 URL 完全一致(包括查询参数),浏览器就只创建并维持一个 Worker 实例。比如:
- https://app.com/sw.js 和 https://app.com/sw.js?v=1 被视为两个不同 Worker,无法共享状态
- http://localhost:3000/sw.js 可用,但 http://127.0.0.1:3000/sw.js 是另一个源,互不通信
- HTTPS 环境下稳定支持;HTTP 仅限 localhost,其他环境静默禁用
顶层变量天然共享,但需手动防竞态
Worker 脚本中定义的顶层变量(如 let count = 0、const cache = new Map())对所有连接的页面可见且共用。这是真正意义上的内存级共享,但 JavaScript 没有原子操作:
- 多个页面同时执行 count++ 可能导致丢失更新
- 简单场景可用标记位(如 isUpdating = true)临时阻塞
- 复杂逻辑建议用队列缓冲指令,由 Worker 串行处理再广播结果
消息通道隔离,状态变更靠主动广播
页面与 Worker 之间不共享函数或引用,所有交互必须走 MessagePort 发送结构化克隆数据:
- 每个页面连接时,Worker 在 self.onconnect 中拿到 e.ports[0],存入集合(如 new Set())
- 状态变更后,Worker 遍历该集合,对每个 port 调用 port.postMessage() 推送新值
- 页面收到消息后自行更新本地 React state 或 Vue reactive 数据,不直接修改 Worker 内变量
生命周期独立,状态持久需额外设计
SharedWorker 不会因某个页面关闭而终止,只有所有端口断开且无定时器/事件监听时才会被回收。但它的内存状态不持久:
- 浏览器休眠、Worker 被系统回收、页面强制刷新都会清空 let 变量
- 关键状态应落地到 IndexedDB 或通过 WebSocket 同步至服务端
- 页面初始化时可先发 { type: 'SYNC_STATE' } 请求当前快照,避免状态错乱











