vue虚拟列表实现
虚拟列表的实现原理
虚拟列表的核心思想是仅渲染可视区域内的元素,而非整个列表。通过动态计算可视区域的起始和结束索引,减少DOM节点数量,提升性能。关键在于监听滚动事件,动态更新渲染范围。
计算可视区域范围
需要获取容器高度、每个列表项的高度以及当前滚动位置。根据这些数据计算当前可视区域的起始索引和结束索引。
const containerHeight = containerRef.value.clientHeight;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight),
listData.length
);
动态渲染可见项
根据计算得到的startIndex和endIndex,从原始数据中切片获取需要渲染的数据。通过CSS的transform属性对切片后的列表进行偏移,保持滚动位置正确。
const visibleData = computed(() => {
return listData.slice(startIndex.value, endIndex.value);
});
const listStyle = computed(() => {
return {
height: `${listData.length * itemHeight}px`,
position: 'relative',
};
});
const itemStyle = (index) => {
return {
position: 'absolute',
top: `${(startIndex.value + index) * itemHeight}px`,
width: '100%',
};
};
监听滚动事件
在容器元素上添加滚动事件监听器,实时更新scrollTop值,触发重新计算可见范围。
const handleScroll = (e) => {
scrollTop.value = e.target.scrollTop;
};
<div class="container" @scroll="handleScroll">
<div :style="listStyle">
<div v-for="(item, index) in visibleData" :key="item.id" :style="itemStyle(index)">
<!-- 列表项内容 -->
</div>
</div>
</div>
动态高度处理
如果列表项高度不固定,需要更复杂的实现。可以预先计算或估计每项高度,存储在数组中。滚动时根据实际高度动态调整。
const heights = ref([]);
const getItemHeight = (index) => {
return heights.value[index] || estimatedHeight;
};
// 在列表项渲染后,测量实际高度并更新
const updateHeight = (index, height) => {
heights.value[index] = height;
};
性能优化建议
使用Intersection Observer API替代scroll事件监听,减少主线程压力。对于超长列表,可以增加缓冲区,预渲染可视区域外的部分项,减少滚动时的闪烁。
const bufferSize = 5;
const startIndexWithBuffer = Math.max(0, startIndex - bufferSize);
const endIndexWithBuffer = Math.min(
endIndex + bufferSize,
listData.length
);






