每天一个前端小知识 Day 33 - 虚拟列表与长列表性能优化实践(Virtual Scroll)_长列表虚拟滚动优化?
虚拟列表与长列表性能优化实践(Virtual Scroll)
🎯 一、背景:为何需要虚拟列表?
在实际业务中,我们经常需要渲染大量数据项,比如:
- 聊天记录滚动展示
- 电商商品长列表
- 数据中心表格(DataGrid)
如果直接将上千条 DOM 节点一次性渲染,会引起:
🧠 二、什么是虚拟滚动(Virtual Scroll)?
虚拟列表是一种“按需渲染”的技术:
通过“视口裁剪 + 滚动同步”,将 10000 项渲染的压力降到几十项以内。
🧩 三、虚拟滚动原理简述
📦 示例:
- 假设每一项高度为
50px
- 列表有 10000 项,总高度为
500,000px
- 实际可视区域只能显示 20 项
👉 只渲染这 20 项 DOM 元素
👉 设置容器滚动高度为总高度
👉 根据滚动位置动态更新“可视区域数据 + offset”
📈 可视区域公式计算:
const visibleCount = Math.ceil(containerHeight / itemHeight);const startIndex = Math.floor(scrollTop / itemHeight);const endIndex = startIndex + visibleCount;
🛠 四、手写简易虚拟列表(原生 JS 示例)
<div id=\"container\" style=\"height:300px; overflow:auto; position:relative;\"> <div id=\"phantom\"></div> <div id=\"content\" style=\"position:absolute; top:0;\"></div></div>
const data = Array.from({ length: 10000 }, (_, i) => `Item ${i}`);const itemHeight = 30;const container = document.getElementById(\'container\');const phantom = document.getElementById(\'phantom\');const content = document.getElementById(\'content\');phantom.style.height = `${data.length * itemHeight}px`;container.addEventListener(\'scroll\', () => { const scrollTop = container.scrollTop; const start = Math.floor(scrollTop / itemHeight); const visibleCount = Math.ceil(container.clientHeight / itemHeight); const end = start + visibleCount; const visibleData = data.slice(start, end); content.style.transform = `translateY(${start * itemHeight}px)`; content.innerHTML = visibleData.map(d => `<div style=\"height:${itemHeight}px\">${d}
✅ 这样页面只渲染有限 DOM,性能稳定,滚动流畅。
⚙️ 五、常见框架中的虚拟列表解决方案
react-window
, react-virtualized
vue-virtual-scroller
virtual-scroll
, 自研实现✅ React 示例(react-window)
import { FixedSizeList as List } from \'react-window\'; {({ index, style }) => Row {index}}
💡 六、进阶优化点(加分项)
IntersectionObserver
动态调整🧪 七、面试高频问题拆解
📌 Q1:你如何优化一个 1 万条数据的滚动列表渲染?
答:
使用虚拟列表技术,仅渲染可视区域内的 DOM 元素,其余通过容器撑高模拟滚动。可结合 react-window 或 vue-virtual-scroller 来高效实现。还可结合懒加载、IntersectionObserver 等增强体验。
📌 Q2:虚拟列表支持不等高的 item 吗?如何处理?
答:
可以,但较复杂。需要:
- 使用
ResizeObserver
或IntersectionObserver
动态记录每项高度 - 使用映射表缓存高度
- 更新滚动偏移时进行累加偏移量校正
更推荐高度一致或接近的场景。
📌 Q3:如何避免滚动白屏或闪烁?
答:
- 设置合理的 buffer 区(上下额外渲染几项)
- 使用
will-change
预优化 transform - DOM diff 使用 key 提升更新效率
- 避免频繁触发
innerHTML
重排