JavaScript 如何做虚拟列表
打开一个装满弹珠的玻璃罐,想要快速找到某颗特定颜色的珠子,你会怎么做?是把所有珠子倒在桌上慢慢翻,还是只盯着罐子开口附近的几颗找?这和前端开发里处理长列表的逻辑是一样的——如果直接渲染10000条甚至100000条数据,就像倒了满桌珠子,页面会卡成“慢动作”。
这时候就需要虚拟列表出马,它本质是“只渲染用户能看到的内容,其他内容藏起来”,用最少的DOM节点实现长列表,让页面跑得飞快。

什么是虚拟列表的核心逻辑
虚拟列表的关键是“两算一留”:算可视区域的范围、算列表总高度、保留少量超出可视区的缓冲项,避免滚动时出现空白断层。
举个例子:你用手机刷短视频,屏幕只能同时显示5个视频,列表总高度是100000px,每个视频高200px,总共有500个视频。当你向上滑动到第10个视频时,虚拟列表不会渲染全部500个,只会计算出当前可视的第10-15个视频,再上下各多留2个缓冲项,最终只渲染14个节点。
这样不管列表多长,页面上始终只有十几个DOM节点,就像你只翻了罐子上半部分的珠子,效率瞬间拉满。
用JS实现虚拟列表的关键步骤
不用复杂框架,纯JS就能实现极简版本,跟着步骤走很容易理解。
第一步,先搭好基础结构:外层是一个固定高度的“可视容器”,用来显示内容;内层是“列表占位容器”,用来撑起整个列表的总高度,让滚动条正常出现。
比如可视容器高500px,每个项高100px,总数据1000条,那占位容器的总高度就是1000×100=100000px,刚好撑起滚动条。
第二步,监听滚动事件,算出当前滚动位置对应的起始项。当可视容器滚动时,滚动距离是scrollTop,用scrollTop除以单项高度,取整数就是起始项的索引,比如scrollTop是250px,单项高100px,起始索引就是2。
第三步,根据起始项渲染可视项+缓冲项。缓冲项的数量不用多,上下各留2个就够,比如起始索引是2,可视项是2-7,加上缓冲项就是0-7,刚好不会出现空白。
第四步,调整占位容器的padding,让可视项刚好“粘”在滚动位置上。这一步是为了让滚动时的位移和真实列表匹配,比如起始索引是2,就把占位容器的paddingTop设为2×100=200px,这样滚动条的位置和真实列表的位置完全对应。
实际开发中的小优化细节
纯手动实现会遇到小问题,实际项目里要加两个小补丁。
第一个是“滚动防抖”:如果不防抖,用户快速滚动时,JS会频繁计算渲染,反而更卡。只要给滚动事件加个定时器,等用户停止滚动100ms再更新,就能减少不必要的计算。
第二个是“项高自适应”:如果列表项不是固定高度(比如有的带图片、有的带按钮,高度不一样),固定单项高的方法就会出错。这时候要改成“提前测量所有项的高度”,或者在渲染时动态获取当前项的高度,再累加计算总高度和滚动位置,虽然多了一步测量,但适配了各种复杂列表。
虚拟列表不是什么高端技术,核心就是“不做无用功”——把用户看不到的内容藏起来,只处理能看见的部分。小到手机的通讯录,大到电商的商品列表,只要是长列表场景,用它都能让页面跑起来更顺。