扫码
40.67M · 2026-04-17
前文提到通过API去滚动容器或容器尺寸去触发打字跟随,用户的滚动去读取造成重排的属性来实现用户是否跟随打字实际会造成浏览器多次重排。
浏览器重排(回流)是浏览器对DOM元素计算位置、尺寸、布局的过程。 浏览器解析HTML合成DOM树,解析CSS合成CSSOM树,它们会一步步合成渲染树,进行布局。页面、结构、尺寸发生变化就会再走一遍布局的计算流程,称为重排。
为什么重排会造成性能开销?
哪些操作会导致重排?
offsetTop / offsetLeft / offsetWidth / offsetHeight
scrollTop / scrollHeight
clientTop / clientWidth 等
getComputedStyle()
getBoundingClientRect()
这里直接取消滚动事件的,在容器的最底部放一个哨兵容器,通过 IntersectionObserver 去哨兵在的父元素在可视区域的交叉值来判断用户是否滚动。让哨兵通过 scrollIntoView 直接暴露在可视区域,实现打字跟随。
<div class="ch@t-scroll-container" ref="scrollContainerRef">
<div class="ch@t-container" id="messagesRef"></div>
<div class="scroll-sentinel" ref="sentinelRef"></div> // 哨兵
</div>
threshold: 1 // 1 :表示全部进入,0 :露头就秒
onMounted(() => {
const ro = new ResizeObserver(() => {
if (enableAutoScroll.value) {
scrollToBottom();
}
});
ro.observe(ch@tMessagesRef.value);
observer = new IntersectionObserver(
(entries) => {
const isIntersecting = entries[0].isIntersecting;
enableAutoScroll.value = isIntersecting;
},
{
root: scrollContainerRef.value, // 父元素,默认为 root
threshold: 1, // 1表示全部进入,0 :露头就秒
rootMargin: '10px', // 提前10px开始生效
}
);
if (sentinelRef.value) observer.observe(sentinelRef.value);
});
const scrollToBottom = () => {
nextTick(() => {
const el = sentinelRef.value;
if (!el) return;
if (enableAutoScroll.value) {
sentinelRef.value.scrollIntoView({ behavior: 'instant' });
}
});
};
在实践过程中,如果使用 behavior: 'smooth' ,浏览器在触发 scrollToBottom 时发生的动画会频繁抖动,将哨兵挤到父容器的可视区域外,导致 IntersectionObserver 频繁触发,可能产生 bug,使用 instant 取消浏览器动画抖动,直接抵达底部,避免该情况产生。