虚拟列表是只渲染可视区域及缓冲区节点、用空白占位其余项的技术,用于解决 大数据 量列表的卡顿、高内存和滚动不流畅问题;通过计算滚动位置下的起始 / 结束索引截取数据,并用 translateY 偏移整体列表实现视觉对齐。

什么是虚拟列表,为什么 需要它
当列表数据量很大(比如上万条),直接渲染所有 dom 节点会导致页面卡顿、内存占用 高、滚动不流畅。虚拟列表只渲染当前可视区域及其少量缓冲区内的节点,其余节点用空白占位,从而大幅减少 DOM 数量和重排 重绘 开销。
核心实现思路:只渲染“看得见”的部分
关键在于计算:当前滚动位置下,哪些数据项在视口内?它们应渲染在什么位置?
- 监听容器的 scroll 事件(或使用 IntersectionObserver 做更精细控制)
- 根据 scrollTop、容器高度、单个 item 高度,算出起始索引 startIndex 和结束索引 endIndex
- 用 slice(startIndex, endIndex) 截取待渲染的数据子集
- 通过 transform: translateY() 或 绝对定位,把列表整体“偏移”到正确位置,让视觉对齐实际滚动
简单可运行示例(固定高度 item)
假设每条数据高度为 50px,容器高 400px,总数据 10000 条:
const container = document.getElementById('list'); const itemHeight = 50; const visibleCount = Math.ceil(400 / itemHeight); // 约 8 条 let startIndex = 0; <p>container.addEventListener('scroll', () => {const scrollTop = container.scrollTop; startIndex = Math.floor(scrollTop / itemHeight); const endIndex = Math.min(startIndex + visibleCount + 2, listData.length); // +2 是缓冲 </p><p>// 渲染 slice 后的 items,并设置 wrapper 的 translateY const fragment = document.createDocumentFragment(); const renderList = listData.slice(startIndex, endIndex); renderList.forEach((item, i) => {const el = document.createElement('div'); el.textContent = item; el.style.height = <code>${itemHeight}px</code>; fragment.appendChild(el); }); listWrapper.innerhtml = ''; listWrapper.appendChild(fragment); listWrapper.style.transform = <code>translateY(${startIndex * itemHeight}px)</code>; });
进阶优化点
真实项目中还需考虑这些细节:
立即学习“Java 免费学习笔记(深入)”;
- 动态高度支持:提前缓存每项高度(如用 map 存 index → height),或结合 ResizeObserver 动态更新
- 防抖 / 节流 scroll:避免高频触发重计算(尤其移动端)
- 使用 requestIdleCallback 或 Web Worker 处理 大数据 切片 和高度预估,避免阻塞 主线程
- 复用 DOM 节点:类似 react 的 key 机制,用池化方式复用已创建的 item 元素,减少创建销毁开销
基本上就这些。不复杂但容易忽略缓冲区、transform 定位和高度缓存——做好这三点,万级列表也能丝滑滚动。