虚拟列表实现
通过只渲染可见区域来优化长列表性能
问题
如何实现虚拟列表来优化长列表的渲染性能?
解答
虚拟列表只渲染可见区域内的列表项,而不是渲染整个列表,从而大幅降低页面渲染复杂度。
实现步骤
1. 计算可见区域
根据容器高度和列表项高度,计算当前可见区域内的列表项数量和起始位置。
const containerHeight = 600; // 容器高度
const itemHeight = 50; // 每项高度
const visibleCount = Math.ceil(containerHeight / itemHeight); // 可见项数量
const startIndex = Math.floor(scrollTop / itemHeight); // 起始索引
const endIndex = startIndex + visibleCount; // 结束索引
2. 渲染可见区域
只渲染计算出的可见区域内的列表项。
function VirtualList({ data, itemHeight, containerHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const visibleCount = Math.ceil(containerHeight / itemHeight);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(startIndex + visibleCount, data.length);
const visibleData = data.slice(startIndex, endIndex);
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: data.length * itemHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${startIndex * itemHeight}px)` }}>
{visibleData.map((item, index) => (
<div key={startIndex + index} style={{ height: itemHeight }}>
{item}
</div>
))}
</div>
</div>
</div>
);
}
3. 动态调整列表高度
设置占位容器的总高度,确保滚动条正确显示。
const totalHeight = data.length * itemHeight;
// 占位容器高度为总高度,内部只渲染可见项
4. 滚动时动态加载
监听滚动事件,根据滚动位置更新可见区域的列表项。
const handleScroll = (e) => {
const scrollTop = e.target.scrollTop;
setScrollTop(scrollTop);
// 根据新的 scrollTop 重新计算并渲染可见项
};
性能优化技巧
- 缓冲区:在可见区域上下各增加几项作为缓冲,减少滚动时的白屏
- 节流:对滚动事件进行节流处理,避免频繁计算
- 缓存:缓存已渲染的列表项,复用 DOM 节点
- 预加载:根据滚动方向和速度预测用户行为,提前加载数据
关键点
- 只渲染可见区域内的列表项,通过
scrollTop和itemHeight计算起始和结束索引 - 使用占位容器撑开总高度,保证滚动条正确显示
- 通过
transform: translateY()定位可见项到正确位置 - 监听滚动事件动态更新可见区域,配合节流优化性能
- 添加上下缓冲区可以减少滚动时的白屏现象
目录