Skip to content

为什么不用占位元素

绝大多数虚拟列表用占位元素撑开滚动空间:一个 overflow: auto 容器,里面放一个 高度等于内容总高的空 div,然后只渲染视口内的行。本库不这么做——滚动位置由 JS 掌管, 内容靠 transform 定位,容器是 overflow: hidden

这一页是那个决定的对照实验。下面左右两侧是同一份 50 万条数据,区别只在怎么撑开 滚动空间:左侧是手写的裸 DOM(overflow: auto + 高占位元素),右侧是本库。

这一页与框架无关

两侧比的是浏览器行为,不是某个框架的 API,所以对照组用原生 DOM 手搭,示例统一以 Vanilla 呈现。结论对 Vue / React 的封装同样成立。

可达性:能客观测量的那部分

50 万 × 100px = 5000 万 px,超过了 Chrome 的元素高度上限 33,554,428px (225,实测值)。

点「两侧同时滚到底」:

  • 对照组scrollHeight 停在 33,554,428,超出的部分被浏览器直接截掉, 尾部约 16 万项永远滚不到
  • 本库 滚到底就是第 499,999 项。

transform 的位移量其实也有同一量级的上限,所以"换成 transform 就没上限"并不成立。 让这条线消失的是不让任何一个 transform 携带完整量级:参与滚动的每一段只带自己的 残差(该段的文档位置 - offset),视口里看得见的东西残差最大就一屏多一点。 100 万项 × 100px(1 亿 px)实测各处定位误差均为 0px。

拖拽露白:只能肉眼看

抓住两侧滚动条快速上下拖拽,对比画面是否闪出空白。

这里刻意没有统计「露白帧」:露白发生在「合成器已经把画面滚过去、主线程还没跟上」 的那一帧里,而任何跑在主线程 requestAnimationFrame 里的探针都只能看到 scroll 事件处理完之后的状态——测出来永远是 0,是个假指标。要逐帧确认请用 DevTools 的 Performance 面板录制后回看。

本库的容器是 overflow: hidden,浏览器不会自己滚动,内容只在 JS 写入偏移量时才移动, 位置与 DOM 永远在同一帧提交——露白从根上不成立。

代价

放弃原生滚动不是只有好处。完整的取舍清单在滚动是怎么工作的一节, 最主要的一条是:拖选文本到容器边缘时不再自动滚动,这是原生滚动免费提供的能力。

微应用尚未挂载。