一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

怎样利用HTML模板构建虚拟列表滚动组件

时间:2026-07-16 08:56:46 编辑:袖梨 来源:一聚教程网

固定行高是虚拟滚动唯一可靠起点,必须严格统一CSS高度、禁用flex自适应等干扰;定位需外层设position:relative;更新前清空旧节点;滚动监听用requestAnimationFrame节流;撑高容器用空div实时更新height。

固定行高 + scrollTop 计算起始索引是唯一可靠起点

别信“自动测高”或“动态高度支持”的宣传——只要行高不固定,Math.floor(scrollTop / itemHeight) 就会失效。浏览器滚动时每帧都要读取 scrollTop,再除以一个变化的 itemHeight,结果就是索引错乱、内容跳变、白屏。真实业务中行高差异超 ±5px 就能肉眼感知“跳行”,而反复调用 getBoundingClientRect() 获取每行高度,会触发强制同步布局(layout thrashing),比全量渲染还卡。

必须提前约定:所有列表项 CSS 高度严格一致,比如 height: 48pxline-height: 48px 并禁用 flex 自适应、min-heightpadding 波动等干扰项。表格场景下还要加 table-layout: fixed 和显式列宽,否则单元格内容撑开会导致高度浮动。

position: absolute 渲染区必须套在 position: relative 容器内

这是最常翻车的定位陷阱。如果外层滚动容器没设 position: relative,你用 toptransform: translateY() 推动内容区,偏移会相对于最近的定位祖先(可能是 body 或某个父级 modal),导致整个列表飘出视口、滚动条失效、甚至空白。

另外,transform: translateY() 在 Chrome 115–122 版本中会意外触发额外合成层,GPU 内存暴涨;top 更可控,且无此副作用。每次更新前务必清空旧节点:contentEl.replaceChildren()(现代浏览器)或 contentEl.innerHTML = ''(兼容 IE11),否则残留事件监听器、Vue/React 的 key 冲突、input 焦点丢失等问题会立刻暴露。

立即学习“前端免费学习笔记(深入)”;

滚动监听必须节流,且优先用 requestAnimationFrame

scroll 事件在 Chrome/Firefox 中每秒可触发 60–120 次,不加限制就直接读 scrollTop + 计算 + 渲染,等于每秒执行几十次 DOM 操作。用 lodash.throttle 是可行的,但更优解是 requestAnimationFrame:它天然与屏幕刷新率对齐,且保证同一帧内只执行一次计算和渲染。

示例逻辑:

let isPending = false;function handleScroll() {  if (!isPending) {    isPending = true;    requestAnimationFrame(() => {      const scrollTop = container.scrollTop;      const startIndex = Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER_SIZE;      const endIndex = Math.min(data.length, startIndex + VISIBLE_COUNT + BUFFER_SIZE * 2);      renderRange(startIndex, endIndex);      isPending = false;    });  }}

注意:缩放页面(如 Ctrl+/-)后,clientHeightitemHeight 实际像素值已变,必须重算 VISIBLE_COUNT 和缓冲区参数,否则可视范围错位。

撑高容器的 phantom 元素不能靠 display: nonevisibility: hidden

它们只是隐藏 DOM,不是删掉。1 万条数据 = 1 万个 div 持续驻留内存,浏览器仍需维护样式树、布局树、图层合成信息,重排重绘开销照旧。虚拟滚动的核心动作是「不创建」,不是「藏起来」。

正确做法是用一个空 div 占满总高度:style="height: ${ITEM_HEIGHT * data.length}px",这个元素只负责撑开滚动条长度,内部不挂任何子节点。它必须是滚动容器的直接子元素,且不能设 overflow,否则会截断内容区。

容易忽略的一点:这个 phantom 高度必须实时响应数据长度变化。如果列表支持增删,每次变更后都要同步更新它的 style.height,否则滚动条长度失准、拖拽错位、到底部触发不了加载。

热门栏目