登录
首页 >  文章 >  前端

WebLocksAPI如何防止多页签冲突

时间:2026-04-27 21:47:41 490浏览 收藏

Web Locks API 并非自动防冲突的“银弹”,而是一个需精密编排的跨标签页互斥信号工具:它本身不拦截 IndexedDB 操作,只有将“打开数据库→开启事务→执行写入→严格 await tx.done”整个流程完整包裹在 await navigator.locks.request('db-write-note-456', ...) 的异步回调中,且所有写入口共用带业务唯一标识(如资源类型+ID)的 URL-safe 锁名,才能真正避免多页签并发写导致的脏数据覆盖;同时必须直面 Safari 全面不支持的现实,强制采用服务端幂等(如 requestId 去重)或 localStorage + storage 事件的降级方案,否则在真实用户场景(私有窗口、跨 iframe、多设备)下,看似严谨的锁逻辑仍将彻底失效。

如何利用 Web Locks API 防止多页签同时向单库发起冲突性的写操作事务

Web Locks API 本身不拦截或阻止 IndexedDB 的写操作,它只提供跨标签页的互斥信号;能否防止冲突,取决于你是否把「打开库→事务→写入→等待 tx.done」整个流程严格包裹在 navigator.locks.request() 的回调中,并且所有写入口都使用同一锁名。

必须 await navigator.locks.request(),不能只调用

常见错误是写了 navigator.locks.request('db-write', cb) 却没 await 它——这会导致锁立即申请又立刻释放,后续的 IndexedDB 操作完全不受控。

  • 正确写法:await navigator.locks.request('db-write-user-123', async (lock) => { /* 所有 db 操作 */ })
  • 回调函数必须返回 Promise,否则锁在函数退出时自动释放,哪怕 put() 还没真正落地
  • 若想支持超时或取消,需传入 { signal: abortController.signal },不能依赖锁自身超时(它没有)

锁名必须带业务唯一标识,不能写死

'db-write' 这种全局锁名,会让用户 A 编辑笔记时,用户 B 更新购物车也被卡住——这不是并发控制,是功能阻塞。

  • 推荐格式:`db-write-${resourceType}-${id}`,例如 'db-write-note-note_456''db-write-cart-789'
  • 锁名需 URL-safe:避免空格、斜杠、控制字符;长度建议 ≤64 字符,过长可能影响内部哈希效率
  • 不同资源类型(如用户资料、日志上报)绝不能共用一个锁名,否则无关操作互相拖慢

务必 await tx.done,而不是仅 tx.commit()

tx.commit() 只是发起提交,不保证写入完成;若此时锁释放,其他标签页可能读到未落盘的中间状态,导致脏数据覆盖。

  • 必须写:await store.put(data, key); return await tx.done;
  • 不要在锁回调里做耗时同步计算(如遍历万级数组)、网络请求或加密运算——这些会延长锁持有时间,放大排队效应
  • onupgradeneeded 不受 Web Locks 管控,多个标签页同时触发版本升级仍可能被浏览器随机拒绝,需在升级逻辑外加降级提示

Safari 不支持,fallback 不是可选项而是必选项

截至 iOS 17.5 / macOS 14.5,Safari 仍未实现 Web Locks API。仅靠 if ('locks' in navigator) 跳过逻辑,等于在 Safari 里完全放弃互斥保障。

  • 服务端幂等是底线:每个写请求附带客户端生成的 requestId,后端校验并去重
  • 前端轻量 fallback 可用 localStorage + storage 事件模拟:写前存 lock:note_456:timestamp,监听到冲突则 UI 提示“正在其他窗口编辑”,但注意这只是最终一致性提示,非强互斥
  • 私有模式窗口、跨 origin iframe、不同设备,全部不共享锁——这些边界情况容易被忽略,但实际线上问题多源于此

今天关于《WebLocksAPI如何防止多页签冲突》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>