虚拟列表是为了解决什么问题
真实项目中的痛点:
想象一个后台系统:用户列表有 10 万条,订单列表 20 万条,日志甚至达到百万级。表格里往往还有多列、复杂 DOM、hover 效果、操作按钮和状态标签。
如果直接 map 渲染:
data.map(item => <Row key={item.id} />)
你会遇到首次渲染卡死、滚动严重掉帧、内存暴涨,甚至浏览器直接崩溃。
根因只有一个:DOM 太多。浏览器不是怕 JS 计算,最怕的是成千上万个 DOM 节点同时存在。
总的来说,虚拟列表就是只渲染可视区域内的列表项,而其余的用占位高度'假装存在'。
虚拟列表的核心思想
要掌握它,得先理清这四个核心概念:
- 可视区域(viewport):屏幕当前能看到的那一段高度。
- 列表总高度(total height):假设所有 item 都渲染后的总高度(假的,但要算出来)。
- 起始索引和结束索引:根据滚动距离,计算现在应该显示哪几条。
- 偏移量(offset / translateY):让当前渲染的 items 看起来在正确的位置。
虚拟列表的本质实现原理
假设每一项高度固定,这是最简单的场景,真实项目中大量列表数据一般也是高度固定的。
itemHeight = 50px
容器高度 = 500px
那屏幕最多能显示:500 / 50 = 10 条。
通常会多渲染几条作为缓冲区:实际渲染 = 10 + 4 = 14 条。
根据滚动距离算索引
startIndex = Math.floor(scrollTop / itemHeight)
endIndex = startIndex + visibleCount
不渲染所有 DOM,但要让滚动条是对的
<div>
<div></div> <!-- 撑开高度 -->
<div></div> <!-- 只放可见项 -->
</div>
{ : totalCount * itemHeight; }

