2026-09-30 17:05:03
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12442
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
scroll-axis-lock 是 CSS Overflow Module Level 5 的新属性,用来控制浏览器是否自动把滚动手势锁定到单一坐标轴。
什么意思呢?
就是说,scroll-axis-lock可以控制用户触摸/拖拽滚动时,是否忽略微小位移。
比方说一个既能水平滚动,又能垂直滚动的容器。
在默认的浏览器行为中,用户向下滚动的时候,毕竟是手动操作,水平方向难免会有微小的位置变化,此时,浏览器会触发轴锁定,会忽略水平方向的位移,让页面以一条直线的方式保持滚动。
这在常规的网页浏览中是极好的体验优化,可以避免手滑带来的斜向漂移。
但是,在地图、画布、大图查看器这类场景,用户就是需要斜向双轴同时滚动,这个默认行为就会干扰。
所以,就需要scroll-axis-lock:none来关闭这种单轴锁定。
我们可以通过下面的视频对比出两者的效果区别(点击播放):
scroll-axis-lock属性的语法如下。
.scroller {
scroll-axis-lock: auto;
}
.map-canvas {
overflow: scroll;
scroll-axis-lock: none;
}
scroll-axis-lock是个非常新的CSS特性,目前只有Chrome浏览器支持,具体如下图所示:

不过,scroll-axis-lock属性本就是一个渐进增强的特性,因此,完全不影响我们在浏览器中使用。
浏览器支持体验更好,如果不支持,那就保持现状。
overscroll-behavior属性也与滚动行为密切相关,只不过overscroll-behavior 控制滚动到边界之后的行为;scroll-axis-lock 控制滚动过程中是否过滤另一轴滚动增量。
以及scroll-axis-lock属性只作用于手势滚动(触摸、鼠标拖拽),不影响鼠标滚轮,滚轮不受这个属性控制。
overscroll-behavior属性也是比较实用的一个属性,可以关闭Mac系统下默认的滚动边界回弹行为,以及关闭滚动左滑到底后触发返回上一页的默认行为。
具体可以访问我之前这篇文章:CSS overscroll-behavior让滚动嵌套时父滚动不触发
本以为上一篇文章是国庆节前最后一篇文章,结果这一篇才是。
因为八成的同事都请假了(可以连休13天),我一个人在公司,实在没有需求,那就写文章代替摸鱼吧,桀桀桀。
好了,其他就不多说了。
大家祝我国庆节继续爆护吧!
分享下最近一个月作钓鱼获。
图一:浦东梅林垂钓园,168六小时,约莫30斤不到点,大口鲶,昂刺鱼和红鲳
图二:浦东清水湾垂钓,120四小时,去的晚,约莫十七八斤,鲤鲫和鳊鱼
图三:浦东繁茂垂钓,238六小时,草鱼鳊鱼红鲳,约莫70斤
图四:嘉定痴渔户外营地垂钓,68元5小时,强回,最终57.6斤,差4两上岸
图五:奉贤兄弟垂钓,180五小时,回鱼3元每斤,最终72斤,上岸小盘
图六:青浦庆同钓场,198五小时,鱼获35斤多,大鳊鱼大板鲫

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12442
(本篇完)
2026-09-29 11:31:56
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12438
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
背景色扩展我们可以使用outline或者box-shadow属性,但是,如果希望是单个方向的扩展,同时不影响现有布局,该如何实现呢?
比方说下面这个使用场景,底部区域sticky粘性定位。
但是由于外部容器设置了padding,导致会有内容从底部区域穿透出来,看起来体验就不怎么好。

比较常用的方法是设置margin负值 + padding值修正。
但这种方法并不怎么优雅,最简单的办法其实就是背景色区域扩展出去,直接覆盖就好了。
不过这里并非四面扩展,outline属性并不支持方位设置。
此时,border-shape和border-image属性就是更好的选择。
核心代码:
.breakout-background {
border-shape: inset(0 -16px -16px) circle(0);
border-color:skyblue;
}
此时的底部布局和交互效果就会是下图GIF这般所示:

相关代码如下:
<aside>
<main>
内容<br>内容...
</main>
<footer>
底部
</footer>
</aside>
aside {
padding: 16px;
border: 1px solid #ccc;
background: aliceblue;
width: 300px; height: 200px;
overflow: auto;
overscroll-behavior: none;
main {
height: 300px;
}
}
footer {
padding-top: 10px;
position: sticky;
bottom: 0;
background: skyblue; /* 兜底,非必须 */
border-shape: inset(0 -16px -16px) circle(0);
border-color: skyblue;
}
CSS border-shape 属性接受两个形状值,前者表示外部形状,后者表示内部形状。
inset() 函数是 CSS Shapes 图形相关的基础形状函数,用来创建内凹矩形裁剪区域。
inset(top right bottom left round radius);
其中,top right bottom left表示的是四个方向向内收缩的距离,注意,是内缩的距离,支持简写,简写规则和 margin /padding 一致。
这里,inset(0 -16px -16px)中的值是负数,因此,形状是外扩,而非内缩,扩展方向是左右和底部。
于是,可以正好覆盖外部容器设置的16px padding大小。
circle(0)表示的是内部形状,表示一个放置在中心的零半径圆。
零半径圆等于没有图形,作用是保证外部形状可以连内部也完全填充。
所以,上述代码中的background: skyblue其实是可以删除的。
border-shape是个很实用的新特性,我三个月前就介绍过了,有兴趣访问这里了解:全新的CSS border-shape属性简介
我看了下,三个月过去了,目前还是仅Chrome浏览器支持。

所以,从实战角度考虑,我们必须考虑其他兼容性更好的方法,那就是border-image属性。
border-image扩展元素的图形区域,这个我很多年前就介绍过,包括我的著作《CSS新世界》中也有详细介绍,是CSS里面极为罕见的不影响排版情况下拓展元素的渲染区域的属性。
唯一的问题就是语法复杂了些,不过现在有AI了,大家只要知道border-image可以做这样的事情,然后让AI去处理就好了。
详见此文:被低估的border-image属性
在本例中,想要扩展元素左右和下方的背景色,可以这么设置。
footer {
border-image: conic-gradient(skyblue) fill 0//0 16px 16px;
}
如果你想装逼,也可以使用image()函数。
image()函数也是个新特性,原本的语法支持URL图像外加剪裁,例如:
image("image1.jpg#xywh=40,0,20,20")
我实际测下来,目前仅支持背景色,Chrome和Firefox浏览器都支持,Safari不支持。
至于具体的兼容性数据,我并没有找到。
代码使用示意:
footer {
border-image: image(skyblue) fill 0//0 16px 16px;
}
效果是一样的。

总结一下,目前任意方向背景色扩展技术,可以在生产环境放心使用的还是border-image属性。
conic-gradient是锥形渐变,也是非常实用的渐变,可以用来绘制饼图,loading图形或者棋盘网格,详见此文:CSS conic-gradient()锥形渐变简介
不过很多开发人员对锥形渐变掌握并不熟,这里替换成线性渐变也是可以的,参考如下:
footer {
border-image: linear-gradient(skyblue) fill 0//0 16px 16px;
}
注意,conic-gradient(skyblue)和linear-gradient(skyblue)模拟纯色背景的语法一开始不是这么精简的,如果你的项目对兼容性要求比较高,稳妥期间,还是使用完整写法conic-gradient(skyblue 0 0)和linear-gradient(skyblue, skyblue)。
好了,其他就没什么了,这应该是国庆节前的最后一篇文章了,祝大家国庆节快乐。
唉,又是一年国庆了,一年又一年,时间过得太快了。
就这样吧,我们下篇文章再见。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12438
(本篇完)
2026-09-17 19:42:29
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12429
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
说起CSS根单位,大家最熟悉的莫过于 rem 单位。
从IE9浏览器就开始支持。
是目前CSS领域最常用的CSS单位之一,尤其在移动端的弹性布局中。
很多人不知道的是,从Chrome 111(2023年3月)、Safari 16.4(2023年3月)、Firefox 120(2023年11月)开始,还支持了其他一系列的根元素单位,五花八门,将目前基于所有与文字相关的CSS相对单位都囊括了。
具体参见:
rcap等于根元素字体字号对应的“大写字母的高度”。
CSS的cap是个比较晚支持的CSS单位,中文场景下此单位出场机会不大,因为表示的是英文大写字母的高度。
其兼容性如下(单位cap和rcap兼容性一致):

rchch表示字符0的宽度,自然,rch就表示当前根元素数字0,Unicode字符 U+0030的宽度。
ch是个IE时代的CSS单位,但是rch却比rlh支持的晚,Safari 17.2(2023年12月)、Firefox 147(2026年1月)才支持.

rem代表根元素(通常是 <html>)的 font-size。在根元素的 font-size 属性内使用时,代表其初始值。浏览器常见默认值为 16px,但用户设置可能改变该值。
rexrex的兼容性和rch是一样的。
ricic这个单位我之前也介绍过,这是与中文环境,东亚语言密切相关的CSS单位。表示字符“水”所占据的宽度。中文字体几乎都是等宽,因此,这个单位可以预估一段纯中文字体占据的宽度。
ric自然就表示根元素字体上“水”这个字符占据的宽度了。
单位ic是2022年各大浏览器开始大规模支持的,目前算是一个可以放心使用的单位了。

至于单位ric的兼容性,则和rex他们一致。
rlhlh和rlh都是新CSS单位,与行高的计算值相关。
然后出乎意料的,lh虽然是新单位,但是rlh的兼容性却比rex、rch这些老单位的根元素表示要好。

苦思冥想,想不到什么好的实战案例。
突然想到了一个办法,问AI。
这是AI提供的案例:
html {
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.6;
}
/* 标题:上下内边距跟随大写字母高度 */
h1 {
padding-block: 1rcap;
}
/* 正文行内图标,高度匹配小写x高度 */
.inline-icon {
height: 1rex;
}
/* 6位数字验证码输入框 */
.code-input {
width: 6rch;
}
/* 中文评论框,最多容纳20个汉字 */
.cn-input {
width: 20ric;
}
/* 段落间距,基于根行高,全局垂直韵律 */
p {
margin-block: 1.5rlh;
}
嗯……怎么说呢?感谢都是非root根元素单位的应用场景。
算了算了,大家了解下吧,至少下次看到这些单位知道是什么意思吧。
关于CSS新单位,几个月前也介绍了一波,有兴趣可以访问“CSS新单位dvh、lvmin、vi、cqb等初解”这里了解。
虽然出了很多CSS新单位,但由于目前的CSS单位够用了,且这些单位大都不是必须不可替代的。
因此,大多都无人问津。
有没有展开介绍的必要。
就这样吧,我们下个视频再见。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12429
(本篇完)
2026-09-15 10:38:59
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12403
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
富文本输入框中的某个<img>元素hover的时候出现如下图所示的黑色提示效果。

如果是传统实现,或者你让AI来实现,多半会在div容器元素上委托mouseenter和mouseleave事件,对于带三角的黑色tips效果,会使用JS动态位置,小三角会使用CSS伪元素创建。
我说的没错吧。
但是,亲们,时代变了啊,前端技术一直在不停发展的哈。
上面那套实现,虽然也能有最终的效果,但是成本、性能和代码量都不怎么行啊。
实际上,以目前的前端能力,可以零JavaScript代码实现本需求。
具体如下:
interestfor属性实现hover悬停目标元素显隐交互,支持设置延时显示时间;border-shape属性绘制轮廓,使边框线条完美;您可以狠狠地点击这里:纯CSS实现黑色气泡tooltip效果demo
最终的效果如下GIF录屏示意:

相关的HTML和CSS代码如下所示:
<button class="button-mention" interestfor="tooltip">黑夜模式封面图</button> <div id="tooltip" class="mention-tooltip" popover="hint">点击 或 按Tab键插入</div>
.button-mention {
padding: 2px 8px;
font-size: 100%;
border: 0;
border-radius: 99px;
background-color: #ffffff26;
cursor: pointer;
opacity: .75;
interest-delay-start: .2s;
color: #fff;
}
.mention-tooltip {
--tooltip-shape: shape( from 5% 0%, hline to 95%, arc to 100% 21.05% of 5% 21.05% small cw, vline to 57.89%, arc to 95% 78.95% of 5% 21.05% small cw, hline to 55%, line to 50% 100%, line to 45% 78.95%, hline to 5%, arc to 0% 57.89% of 5% 21.05% small cw, vline to 21.05%, arc to 5% 0% of 5% 21.05% small cw, close );
position: fixed;
position-area: top;
width: max-content;
margin: 0 0 2px;
padding: 2px 8px 8px;
border: 0;
aspect-ratio: 4.211;
background-color: #000;
color: #fff;
font-size: 12px;
clip-path: var(--tooltip-shape);
line-height: 2;
&:interest-target {
animation: tinydown 0.2s ease-in-out;
}
}
@supports (border-shape: none) {
.mention-tooltip {
clip-path: none;
border: 1px solid #fff1;
border-shape: var(--tooltip-shape);
}
}
@keyframes tinydown {
from {
transform: translateY(-5px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
这里的tooltips效果几乎可以说是集这几年前端新特性的大成之作。
此案例使用的新特性我在之前的文章都有介绍过。
这综述下:
按钮和链接元素设置HTML interestfor属性,可以让元素在鼠标移入移出的时候,自身和目标元素产生变化。
如果目标元素是popover元素,则目标元素会自动显示与隐藏。
也就是interestfor交互中,目标元素的popover属性不是必须的。
详见此文:HTML interestfor属性与悬停popover交互效果
额外说明:
如果是点击行为交互,则只能是按钮元素与popovertarget属性,链接元素不支持。
popover交互出现比较早,目前来说已经是相对成熟的技术了。
其中,本案例用到的popover="hint"则是去年才支持的新特性,其作用是,popover浮层显示的时候,不会隐藏之前的浮层,特别适合用在tooltip这种轻提示的场景。
有兴趣可以访问这里了解:HTML popover再进化 – 新增hint类型提示框
popover元素默认是屏幕居中显示的,我们这里的诉求是在按钮上方显示,需要重定位。
这是CSS锚点定位的强项了。
这里的定位比较简单固定,因此,直接一行position-area:top就可以了。
CSS锚点定位是前端必学的技术,LuLu UI组件库的定位已经全部改成CSS锚点定位了,可以节约大量的代码,也不用担心滚动嵌套,以及复杂HTML定位元素嵌套的问题。
文章见:告别JS浮层,全新的CSS Anchor Positioning锚点定位API
仔细看设计图,黑色提示框一圈是有一圈浅色的边框的。
在过去,我们多使用filter: drop-shadow()多投影去模拟,但是效果只是近似,并不完美。
现在又了border-shape属性,自然天然支持。
只不过这里想要得到提示框对应的shape()函数路径参数是实现的难点。
根据我的测试,几乎所有的模型全都全军覆没。
因为shape()函数中的SVG路径语法是一个全新的语法,AI生成的代码看似有模有样,但都跑不起来。
我最终是怎么实现的呢?
让AI去生成传统SVG的提示框路径代码,然后使用我自己弄的“CSS clip-path path() to shape()函数转换工具”转换得到的。

不过由于border-shape属性特别新,Safari浏览器还没有支持。
因此,最后使用了clip-path属性兜底。
详见文章:
width:max-content这个CSS声明支持有快10年的历史了,日常开发我们用得不多,并不是因为他不好用,而是大家习惯使用white-space:nowrap实现类似的效果。
我个人推荐使用width:max-content,会显得更高级,且遇到绝对定位拉伸场景的时候,尺寸也不会被随意改变。
aspect-ratio是一个次新的CSS属性,用来控制元素的高宽比例,可以取代具体高宽设置,让CSS代码的适应性更强。
如何在 vibe 编程的时候,让 AI 输出比较前沿但是效果和代码都非常好的技术实现呢?
首先第一点,也是最最重要的。
那就是开发者自己必须要知道此需求可以使用何种技术实现,也就是必须要知识广度的积累。
这就是我一直不停学习的原因。
不过,AI总是倾向于采用传统稳健的实现方法,因此,想要让他输出高级的实现方法,一定要好好调教。
首先,按照MDN官方的skills,这个一定要安装,不然AI会产生自以为是的幻觉。
不过,根据我的实操,即使有了MDN skills的学习与约束,AI还是会有很多自以为是。比方说本例中,hover显隐完全CSS就可以cover,AI生成的代码居然自己加了JS代码mouseleave隐藏,多此一举。
导致我需要刻意强调一遍。
以下是我使用国产混元4模型(最近免费)实现tooltips效果的实录。
第一轮:
下面实现
button.button-mention的tooltips效果。此效果完全采用新技术实现,没有任何JS交互的参与。
- Hover
<button>元素的显示与隐藏,使用interestfor属性实现。所有的按钮的interestfor都指向同一个元素:
<div id="mentionTooltip" class="mention-tooltip" popover="hint">点击 或 按Tab键插入</div>mentionTooltip元素使用JS动态创建,只需要插入一次,可以在第一次创建button.button-mention元素的时候执行。此元素可以放在<body>元素下。interestfor+popover只是解决了Hover显隐的问题,并未解决定位的问题。所以,还需要使用CSS锚点定位,将
#mentionTooltip定位在<button>元素的上方。CSS锚点定位已经出来了一两年时间,你这边应该实现没问题,我就不细节指导了。
- 补充一点,Hover按钮显示Tooltip,点击的时候按钮不是会消失吗?这个Tooltip无需再额外JS隐藏,因为这是浏览器内置的交互行为,懂否?不是自定义组件那套交互,千万不要自以为是,额外添加冗余代码。
- tooltips的图形效果也使用全新的技术实现。具体如下:
- 如果用户浏览器支持CSS
border-shape属性(可以使用@supports语法判断),那就使用border-shape一次性绘制到位。这个已经超出了你的能力范围,所以,核心代码我帮你写好了:
.mention-tooltip { aspect-ratio: 4.211; border: 1px solid #fff1; border-shape: shape(from 5% 0%,hline to 95%,arc to 100% 21.05% of 5% 21.05% small cw,vline to 57.89%,arc to 95% 78.95% of 5% 21.05% small cw,hline to 55%,line to 50% 100%,line to 45% 78.95%,hline to 5%,arc to 0% 57.89% of 5% 21.05% small cw,vline to 21.05%,arc to 5% 0% of 5% 21.05% small cw,close); }- 如果不支持CSS
border-shape,那就使用clip-path: 代码示意:.mention-tooltip { aspect-ratio: 4.211; clip-path: shape(from 5% 0%,hline to 95%,arc to 100% 21.05% of 5% 21.05% small cw,vline to 57.89%,arc to 95% 78.95% of 5% 21.05% small cw,hline to 55%,line to 50% 100%,line to 45% 78.95%,hline to 5%,arc to 0% 57.89% of 5% 21.05% small cw,vline to 21.05%,arc to 5% 0% of 5% 21.05% small cw,close); }
border-shape和clip-path的shape()函数代码是一样的。因此,为了节约代码,
shape()函数可以提取为CSS变量。
.mention-tooltip剩余的背景色(纯黑)和文字颜色(纯白)以及fixed定位等CSS,你就按照你的理解补充,这个不难。对应CSS写在canvas中。
最后再强调一点,此需求其实代码量很少。
有什么疑问可以提出来,如果没有疑问,帮你的实现清单给我看一下,我确认下。
第二轮:
- constants.ts 新增两个常量这个没必要。
MENTION_TOOLTIP_ID和MENTION_TOOLTIP_TEXT只会用在一个地方,没有必要提取,这是属于过度提取,反而增加了代码的复杂度。
btn.title冲突这么处理,如果浏览器支持interestfor属性,那不设置title,否则使用浏览器原生的title属性。- 显示延迟的CSS可以加,200ms足够了。
- 文案是固定的。
第三轮:
btn.title实现代码还有些问题,手动修改。done!
根据我最近的使用,混元4模型还是可以用的,大部分场景的需求是可以满足的。
不足就是思考时间太长,高端与前沿的需求还是差些火候。
不过最近免费不要钱,这些不足也是可以忍一忍的。
简单项目AI生成的代码规整,完美,无懈可击。
然而多人合作的复杂项目,你使用A模型,我使用B模型,再加上很多开发无脑accept,时间一长,最终代码惨不忍睹,虽然也能运行,但是性能差到离谱(参见文末图片)。
我现在让AI动手编程之前,一定会让他陈列修改文件清单,评估他的实现。
差不多一大半的时间都在沟通讨论,纠正调教。
今天试用了GLM-5.3,能力比混元强,但是思考时间也长,token消耗也高。
还是Claude Code好用,不过这个贵,在公司开源节流的情况下,还是需要考虑其他模型。
这周再试用下 deepseek 4.1。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12403
(本篇完)
2026-09-10 18:37:44
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12396
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
大文件上传,为了用户体验良好,通常需要接入断点续传。
相关前端实现我十几年前就有介绍过“HTTP协议下文件上传断点续传”。
不过本文要讲的重点不是断点续传本身,而是其中可能遇到的一个用户体验问题。
那就是如果用户同时打开多个同一个URL地址的文件续传标签页,此时该怎么办?

如果我们放任不管,那么这几个页面都会自主主张给后端发送上传数据。
那还得了,每个页面的上传进度不一样的,后端返回岂不是一会儿30%,一会儿25%,一会儿又是35%。
于是就会看到进度条像得了羊癫疯一样,疯狂跳动,来,社会摇,走起来。
这种疯癫的交互效果就算是精神小妹都无法容忍。
显然需要处理?
该怎么处理呢。
我们不妨看看市面上现有的,跨页面的通信交互方案,能不能优雅地解决此问题。
// 示意代码,勿使用
if (!localStorage.getItem('upload-lock')) {
localStorage.setItem('upload-lock', Date.now());
// 开始上传
// ...
}
问题在于:
// 逻辑:读取计数 +1 再存回去
let count = Number(localStorage.getItem('counter')); // ①读
count = count + 1; // ②内存修改
localStorage.setItem('counter', count); // ③写
上面代码的读本身,和写本身可以看成是原子操作,但是,整个任务却不是原子的,因为读和写中间有一段业务计算,整套操作被拆分,中间可以被别的标签页抢占。
广播通道API我去年也介绍过,详见“Broadcast Channel API简介,可实现Web页面广播通信”一文。

如果是针对当前需求,我们可能会这么处理:
const channel = new BroadcastChannel('upload-coordination');
channel.postMessage({ type: 'REQUEST_UPLOAD_PERMISSION' });
看起来代码还挺简单,不过问题也比较致命。
那就是同时发送的消息可能会造成冲突,需要手动处理心跳检测、崩溃检测和冲突解决。
可以看到,那些常见的跨页面通信手段,都无法真正解决这里的共享文件上传问题。
其实,类似这种多页面操作统一资源的场景,毫无疑问,最好的解决方案就是Web Locks API。
“锁”是计算机开发中的一个重要概念,前端开发人员接触的不多,但是如果是数据库开发,那这个可太熟了。
锁其实是一种机制,它确保共享资源(变量、对象、函数调用等)一次只能被一个对象访问,并且保证每个调用者都能获得最新信息。
比方说:一家银行在共享内存中存储了多个账户,并且有多台ATM机同时进行取款、存款和查询余额的操作。
如果没有协调(同步),这将导致混乱,如下图所示。

此时,我们可以通过锁定账户的读写权限来控制访问。
这样一来,无论执行什么操作(取款、充值或查询余额),第一个获得锁的调用者都将获得该锁。
其他调用者必须等到第一个获得锁的调用者完成操作后,锁才会被释放。
下图展示了同步账户访问后的情况。

OK,万事具备,就看Web Locks API如何在代码层面解决我们的上传冲突问题了,其实代码很简单。
// 只有成功获取锁才继续处理
navigator.locks.request(LOCK_NAME, { ifavailable: true }
async (lock) => {
if (lock) {
// 执行上传
upload();
await holdLockUntilComplete(abortController);
}
// 获取不到锁则静默跳过 - 其他标签页正在处理
// ...
}
);
看到没有,就几行代码。
使用非常简单,就是把原来的实现代码,使用navigator.locks.request()方法包一下就好了。
此时,浏览器会自动加锁和锁判断。
锁的生命周期管理示意
上述代码中的holdLockUntilComplete()可以用来中断上传请求,或者用来管理整个上传的生命周期。
使用示意,供大家参考。
const holdLockUntilComplete = (abortController) => {
return new Promise((resolve) => {
const cleanup = () => {
// 移除所有的监听事件
uploader.off("complete", cleanup);
resolve();
};
// 上传完成事件
uploader.on("complete", cleanup);
// 其他事件略...
// 组件卸载时也释放
abortController.signal.addEventListener("abort", cleanup);
});
};
navigator.locks.request()方法是支持一些可选参数的,语法如下:
request(name, callback) request(name, options, callback)
其中,options支持以下一些参数,这些参数大多数场景下都用不到,大家一眼扫一下就可以了。
"exclusive" 或 "shared" 之一。默认值是 "exclusive",表示排他,也就是一次只能一个锁。"shared" 表示共享锁。
如果为 true,则只有在尚未持有锁的情况下才会授予锁请求。如果无法授予,则将使用 null 而不是 Lock 实例来调用回调。默认值为 false。
如果为 true,将释放所有同名已持有的锁,并授予该请求。默认值为 false。
警告:小心使用!之前在锁内运行的代码会继续运行,并且可能与现在持有锁的代码发生冲突。
一个 AbortSignal(AbortController 的 signal 属性);如果指定并且 AbortController 被中止,则锁请求将被丢弃(如果尚未授予)。
除了本文提到的大文件断点续传,还有以下这些需求场景适合使用 Web Locks API.
还有下图所示的情况,也有必要上锁

Web Locks API已经出现很多年了,兼容性还是相当OK的,以目前的AI能力,此API使用是没有任何困扰的。

除了上面介绍过的request()方法,navigator.locks还有个query()方法,此方法执行后返回一个个Promise,该Promise解析后得到一个对象,其中包含有关已持有锁和挂起锁的信息。
具体有哪些信息,不展开介绍,因为没有意义,正遇到类似需求,直接让AI去运行代码就好了。
好了,本文内容已经比预期的多多了。
其实没必要扯那么多东西的,如今AI这么强,技术细节并没有那么重要。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12396
(本篇完)