vue虚拟列表实现原理
虚拟列表的实现原理
虚拟列表(Virtual List)是一种优化长列表渲染性能的技术,通过仅渲染可视区域内的元素,减少DOM节点数量和内存占用。Vue中可通过计算可见区域的数据索引动态渲染内容。
核心实现步骤
计算可视区域范围
通过监听容器的滚动事件,获取滚动距离scrollTop和容器高度clientHeight,计算出当前可视区域的起始索引(startIndex)和结束索引(endIndex)。
const handleScroll = () => {
scrollTop.value = containerRef.value.scrollTop;
startIndex.value = Math.floor(scrollTop.value / itemHeight);
endIndex.value = Math.min(
startIndex.value + Math.ceil(visibleHeight.value / itemHeight),
listData.length
);
};
动态渲染可见项
根据startIndex和endIndex从完整数据中切片出可见部分,通过v-for渲染。同时通过CSS调整列表项的定位(如transform: translateY),模拟完整列表的高度。
const visibleData = computed(() => {
return listData.slice(startIndex.value, endIndex.value);
});
设置占位容器
使用一个具有总高度的占位元素(如<div class="phantom">)撑开滚动条,其高度为itemHeight * listData.length。
<div class="phantom" :style="{ height: totalHeight + 'px' }"></div>
性能优化点
- 固定项高度:若项高度固定,可直接通过乘法定位;若动态高度需维护位置缓存。
- 滚动节流:使用
requestAnimationFrame或lodash.throttle减少滚动事件触发频率。 - 复用DOM:通过
key属性复用已渲染项,避免频繁创建/销毁DOM。
动态高度处理
对于不定高项,需维护一个位置缓存数组(positions),记录每一项的累积高度,滚动时通过二分查找确定startIndex。
// 初始化位置缓存
const initPositions = () => {
positions.value = listData.map((item, index) => ({
index,
height: defaultHeight,
top: index * defaultHeight,
bottom: (index + 1) * defaultHeight,
}));
};
示例代码结构
<template>
<div class="virtual-list" @scroll="handleScroll" ref="containerRef">
<div class="phantom" :style="{ height: totalHeight + 'px' }"></div>
<div class="content" :style="{ transform: `translateY(${offset}px)` }">
<div v-for="item in visibleData" :key="item.id" class="list-item">
{{ item.content }}
</div>
</div>
</div>
</template>
通过上述方法,虚拟列表能在万级数据量下保持流畅渲染,适用于表格、下拉选择等场景。







