虚拟滚动是解决海量数据渲染卡顿的核心方案,它只渲染视口内可见部分,通过固定容器高度、占位div模拟总高、scrollTop计算索引、transform定位行元素实现动态渲染,需固定行高、避免重排,并推荐使用react-window、vue-virtual-scroller等成熟库。

虚拟滚动是解决海量数据渲染卡顿的核心方案,它不渲染全部数据,只渲染视口内可见的部分,大幅减少 DOM 节点数量和重排重绘开销。
理解虚拟滚动的核心原理
虚拟滚动不是“懒加载”,而是“位置驱动的动态渲染”:表格容器保持固定高度,内部用一个超长的占位 div 模拟整体高度(例如 10 万行 × 行高 = 200 万像素),再通过 scrollTop 计算当前可视区域起始索引,仅渲染该区域内约 10–20 行真实 DOM,并实时更新其 transform: translateY() 偏移位置来对齐视觉。
关键点:
- 容器必须设置 overflow-y: auto 且有明确高度(不能是
height: fit-content) - 所有行元素需使用 position: absolute 或 transform 定位,避免触发全局重排
- 滚动事件不直接监听
scroll,推荐用requestIdleCallback或节流 +IntersectionObserver辅助优化
手写简易虚拟列表(适用于自定义表格)
适合需要完全控制样式、交互或集成复杂组件(如可编辑单元格、嵌套展开)的场景。核心四步:
立即学习“Java免费学习笔记(深入)”;
-
计算可视区域:根据容器高度、行高、scrollTop,得出
startIndex = Math.floor(scrollTop / rowHeight),再结合缓冲行数(如 ±5)确定渲染范围 -
生成真实 DOM 片段:只对
[startIndex - buffer, startIndex + buffer + visibleCount]内的数据调用renderRow(item, index) -
定位每行位置:给每个
<div>设置style.transform = `translateY(${index * rowHeight}px)` -
监听滚动并更新:在
scroll回调中重新计算 startIndex 并批量更新 DOM(建议用DocumentFragment或innerHTML替换局部)
注意:行高必须固定(或预存各行列高数组)。若行高动态,需配合 ResizeObserver 缓存并重建位置映射表。
用成熟库快速落地(推荐 React/Vue 场景)
生产环境优先选用经过验证的库,省去边界处理(如 scrollToIndex、空闲加载、键盘导航支持):
- React:用 react-window(轻量、API 清晰)或 react-virtualized(功能全但稍重)。两者都支持 variable-size 列表、滚动到指定项、动态加载等
- Vue:推荐 vue-virtual-scroller(兼容 Vue 2/3)或组合式 API 下用 @tanstack/vue-virtual(基于 TanStack Virtual,类型友好、扩展性强)
-
原生 JS:可封装 virtuoso 的
Virtuoso组件(支持纯 HTML 模式),或用 intersection-observer + 手动管理渲染区
表格场景的特别优化技巧
虚拟滚动用于表格比列表更复杂,因需对齐列宽、处理表头冻结、支持横向滚动。不建议从零实现,而应:
- 用专为表格设计的库:如 AG Grid(Enterprise 版含虚拟滚动)、Tabulator(开源,内置 virtual DOM 模式)
- 若必须自研,将表格拆成三部分独立滚动:固定表头(
position: sticky)、左侧冻结列(position: sticky)、主体内容区(虚拟滚动容器) - 列宽统一用
flex-basis或minmax()配合table-layout: fixed,避免因内容导致重排;文字溢出一律text-overflow: ellipsis
滚动性能瓶颈常来自 CSS:禁用 box-shadow、filter、border-radius 等触发层合成的属性,必要时加 will-change: transform 提示浏览器。



















