web locks api不能直接操作数据库,仅提供同源跨标签页及pwa实例(含service worker、web worker)的互斥锁机制;需将indexeddb“打开→读取→修改→写入→await tx.done”全流程包裹在navigator.locks.request()回调中,锁名须按业务粒度设计(如user-${userid}-profile),并为safari等不支持环境实现服务端幂等或版本号校验降级。

Web Locks API 本身不操作数据库,它只提供跨标签页、跨 PWA 实例的互斥锁机制。PWA 实例(包括主页面、后台 Service Worker、Web Worker)只要同源,就能通过同一把锁名实现协调——关键在于把 IndexedDB 的「打开 → 读取 → 修改 → 写入 → 等待事务完成」整个流程完整包裹在 navigator.locks.request() 回调中,并严格 await tx.done。
必须串行化的写操作要包全整段逻辑
不能只锁“写入”那一步,否则锁形同虚设。常见错误是:
- 只对
store.put()加锁,但没等事务落地就退出回调 - 在锁内调用
indexedDB.open()后直接返回,没等待数据库打开完成 - 用了
tx.commit()而非await tx.done,导致锁提前释放,其他实例读到旧值再覆盖
正确做法是:所有异步步骤(开库、启事务、读、写、等提交)都放在锁回调里,且每一步都 await。
锁名要带业务粒度,避免全局阻塞
用静态锁名如 'idb-write' 会强制所有写操作串行,哪怕修改的是不同用户或不同文档。推荐按资源维度命名:
- 用户级操作 →
`user-${userId}-profile` - 文档草稿 →
`draft-${docId}` - 配置项 →
`settings-theme-${userId}`
这样多个 PWA 实例编辑不同用户的资料时互不影响,只有真正竞争同一数据时才排队。
Service Worker 和主线程要共用同一套锁逻辑
Service Worker 可能因后台同步、推送通知等触发写操作,它和主线程属于同一 origin,锁名一致即可参与协调。注意:
- 不要在 SW 中用
self.clients.matchAll()去“通知”主线程——这不可靠,也不必要;锁本身已保证逻辑串行 - 确保 SW 和页面都使用相同版本的 IndexedDB schema,避免
versionchange事务冲突 - 若 SW 需在无用户交互时写数据(如定时同步),锁仍有效,但需注意锁请求可能被浏览器限制在活跃上下文中(部分 Android WebView 有例外)
兼容性与降级必须主动处理
Safari 16.4+ 支持,Chrome/Firefox/Edge 较新版本稳定可用;Safari 早期版或私有窗口中 navigator.locks 为 undefined。不能跳过,要降级:
- 检测不支持时,改用服务端幂等接口:前端生成唯一
requestId,后端拒绝重复 ID 的写请求 - 或退回到乐观更新 + 版本号校验(如存
_rev字段,写前比对) - 避免用 localStorage 模拟锁——它同步、无事务、无法被 locks 协调,只会掩盖问题
不复杂但容易忽略。










