web locks api 必须通过 javascript 的 navigator.locks.request() 调用,不支持 html 属性;需 await、回调返回 promise、锁名绑定具体资源且 url-safe;safari 不支持;防覆盖须配合 indexeddb 并 await tx.done。

Web Locks API 不能在 HTML 中直接使用——它没有 HTML 标签、属性或事件,必须通过 JavaScript 调用 navigator.locks.request() 才能生效。把锁逻辑写进 <script></script> 里是唯一可行方式,且需满足安全上下文(HTTPS 或 localhost)。
为什么不能在 HTML 标签里加 lock 属性或 data-lock
Web Locks API 是纯 JavaScript 接口,浏览器不解析任何 HTML 属性来触发锁行为。试图写 <button data-lock="cart"></button> 或 <div lock-name="user-preferences"> 完全无效,这些字符串只是静态文本,不会注册锁、不会排队、也不会阻塞其他标签页。
<ul>
<li>所有锁操作必须显式调用 <code>navigator.locks.request(),且返回的是 Promise
'user-cart-123')必须由 JS 动态生成并传入,不能硬编码在 HTML 结构中
navigator.locks.request() 必须 await,否则锁立即释放
常见错误是调用后不 await,导致锁在函数退出瞬间就释放,根本起不到互斥作用。尤其在事件处理器中极易踩坑:
button.addEventListener('click', () => {
// ❌ 错误:没 await,锁秒放
navigator.locks.request('cart-write', () => updateCart());
// ✅ 正确:必须 await,且回调要返回 Promise
navigator.locks.request('cart-write', async () => {
await updateCart(); // updateCart() 内部必须是异步操作
});
});
- 不
awaitnavigator.locks.request(),等于没加锁 - 回调函数必须返回 Promise(如
async () => {...}或显式return Promise.resolve()),否则锁在同步代码执行完后立刻释放 - 锁释放时机 = 回调返回 Promise 并 resolve/reject 后,不是函数开始执行时
锁名必须带业务标识,不能写死成 'lock'
用固定字符串如 'lock' 或 'data' 作锁名,会导致所有用户、所有资源共用同一把锁——A 用户编辑购物车时,B 用户连个人设置都保存不了。
- 正确做法:锁名绑定具体资源,例如
`cart-write-${userId}`、`prefs-${accountId}` - 锁名需 URL-safe:避免空格、斜杠、控制字符;可对用户输入做
encodeURIComponent()清理,但非必需 - 长度建议 ≤64 字符;过长(如 >256)可能影响内部哈希性能,且无实际语义增益
- Safari 完全不支持该 API(截至 iOS 17.5),仅靠
if ('locks' in navigator)判断跳过逻辑,等于放弃所有协调保障
真正串行化写入,必须搭配 IndexedDB 而非 localStorage
很多人以为给 localStorage.setItem() 加锁就能防覆盖,结果发现照样丢数据——因为 localStorage 是同步阻塞 API,锁根本插不进它的执行流。
-
localStorage.setItem()在调用瞬间就完成,navigator.locks.request()的异步机制无法干预 - 真正能串行化的方案是:用锁包裹「打开 IndexedDB → 读取 → 修改 →
put()→await tx.done」整段流程 - 必须
await tx.done,只调用tx.commit()不够——后者不保证写入落地,锁提前释放会导致其他标签页读到脏数据 - 若非得用
localStorage,只能做轻量提示(如存lock:cart-${id}+ 监听storage事件),但这不防止覆盖,仅用于弹窗提醒
最容易被忽略的是锁的语义边界:它只保证“最多一个标签页执行某段 JS”,不保证数据最终一致。你得自己确保那段 JS 真的完成了原子写入——比如漏了 await tx.done,或者把耗时计算塞进锁回调里,都会让锁形同虚设。











