web locks api需与indexeddb事务配合实现pwa多实例写操作串行化:锁名须按业务粒度命名(如'cart-write-user-123'),写操作须await tx.done,safari不支持时需降级至服务端幂等处理。

Web Locks API 能有效协调多个 PWA 实例(即多个标签页、窗口或后台 Service Worker)对本地数据库(如 IndexedDB)的写操作竞争,但前提是必须正确组合使用 —— 它本身不操作数据库,只提供跨上下文互斥机制;真正保证写入原子性的是 IndexedDB 的事务,而锁的作用是把「打开库 → 读取 → 修改 → 提交事务」这一整段逻辑串行化。
必须用 IndexedDB,不能用 localStorage
localStorage 所有方法(setItem、getItem 等)都是同步阻塞调用,navigator.locks.request() 是异步 Promise 接口,根本无法干预其执行时机。即使加了锁,多个 PWA 实例仍会在毫秒级内先后进入回调并立即写入,结果仍是覆盖而非串行。所以第一步是迁移存储方案:
- 将用户偏好、购物车、草稿等结构化状态从 localStorage 迁移到 IndexedDB
- 确保每个写操作都封装在 readwrite 事务中,利用其单次 put() 的原子性
- 避免在锁内做任何同步耗时操作(如 JSON.parse 大字符串、遍历长数组)
锁名要带业务粒度,避免全局阻塞
锁名不是随便起的字符串,它决定了竞争范围。用静态名(如 'db-write')会让所有 PWA 实例排队写任意数据,严重降低并发能力:
- ✅ 推荐:按资源维度命名,例如 'cart-write-user-123'、'draft-update-abc456'
- ❌ 避免:'idb-write' 或 'update-all' —— 不同用户的购物车不该互相阻塞
- 锁名需 URL-safe,不含空格或控制字符;长度适中,便于调试和监控
写操作必须 await tx.done,不能只 await store.put()
IndexedDB 事务的 commit() 只是发起写入,不代表已落地;tx.done 才是 Promise,表示事务真正完成。若不 await 它,锁会在数据实际写入前就释放,其他实例可能读到旧值或引发竞态:
- 正确写法:await store.put(value, key); await tx.done;
- 错误写法:仅 await store.put(...),或写成 tx.commit()(它不返回 Promise)
- 整个逻辑必须包裹在 navigator.locks.request() 的 async 回调中,并 return 或 await 其结果
兼容 Safari 与降级策略不可少
Safari(截至 iOS 17.5 / macOS 14.5)完全不支持 Web Locks API。纯前端强一致在 Safari 下不可行,需设计 fallback:
- 检测支持性:if ('locks' in navigator),否则跳过锁逻辑
- 服务端兜底:所有写请求附带唯一 requestId 和客户端时间戳,后端做幂等处理或 ETag 校验
- 前端轻提示:用 localStorage 存临时标记(如 'lock:cart-123:1714438500'),监听 storage 事件发现冲突时提醒用户“其他窗口正在编辑”










