Shared Worker 是浏览器中唯一能天然串行化多标签页 IndexedDB 写操作的机制,通过集中收口请求、FIFO 排队执行事务、配合 Web Locks API、持久化关键状态及全量错误广播,构建可控可恢复的单线程写入流水线。

Shared Worker 是目前浏览器中唯一能天然串行化多标签页对 IndexedDB 写操作的机制。它不靠加锁,而是把所有写请求收口到一个中心线程里排队执行,从根本上避免并发写入导致的数据覆盖、丢失或状态错乱。
让 Shared Worker 成为 IndexedDB 的唯一代理入口
每个页面不再自己调用 indexedDB.open() 或开启事务,而是统一通过 port.postMessage({ type: 'write', store: 'users', key: 123, value: {...} }) 发起请求。Shared Worker 收到后,只维护一个待处理队列,按先进先出顺序依次打开数据库、执行 readwrite 事务,并在事务完成(或失败)后再处理下一条。
- 所有写操作必须走同一份 Worker 脚本,确保逻辑集中、路径唯一
- 初始化时打开数据库连接并复用(避免频繁 open/close 开销),但每次事务仍需独立创建
- 对于需要读-改-写的操作(如计数器自增),务必在事务内完成整套逻辑,不暴露中间状态
配合 Web Locks API 做双重保障(可选但推荐)
虽然 Shared Worker 已保证串行,但在极端场景(如 Worker 重启、页面异常断连再重连)下,仍可能因状态未持久化引发竞态。此时可在事务外加一层锁:
- 在 Shared Worker 中调用
navigator.locks.request('db-write-lock', async () => { ... }) - 锁的 scope 推荐细化到具体 objectStore(如
'lock:users'),而非全局一把锁 - 锁只用于协调“谁有资格发起本次写”,实际写逻辑仍在 IndexedDB 事务内完成
关键状态必须落地,不能只靠内存变量
Shared Worker 的内存不可靠:浏览器可能随时终止空闲 Worker,所有页面关闭后实例即销毁。因此:
- 初始化时从 IndexedDB 加载最新快照,作为 Worker 内部状态的起点
- 每次写成功后,主动触发一次轻量同步广播(如
{ type: 'sync', store: 'settings', data: updatedValue }),供新连接的页面快速对齐 - 避免在 Worker 中缓存大量数据;只保留必要元信息(如最后更新时间、当前版本号),真实数据始终以 IndexedDB 为准
错误处理要面向全量页面,不止单个请求者
当某次写入失败(如 DB 被用户手动清除、版本升级冲突),Shared Worker 不应仅回复报错给发起页面,而应:
- 广播
{ type: 'db-error', reason: 'version-mismatch' }给所有已连接端口 - 触发各页面本地清理缓存、重置 UI 状态
- 提供恢复入口(如自动重试初始化或引导用户刷新)
本质上,Shared Worker 在这里不是“锁服务”,而是“调度中枢”——它把原本分散、不可控的多点写入,变成一条可控、可审计、可恢复的单线程流水线。











