什么是虚拟列表
约 727 字大约 2 分钟
2025-03-28
一、什么是虚拟列表?
虚拟列表(Virtual List)是一种 优化长列表渲染性能 的技术,它通过仅渲染当前可视区域内的元素(比如屏幕中可见的 10-20 条数据),而不是渲染整个列表(比如 10,000 条数据),从而大幅减少 DOM 节点数量和内存占用,提升页面流畅度。
二、核心实现步骤
1. 计算可视区域范围
- 可视区域高度(viewportHeight):用户当前看到的列表容器高度(如
500px
)。 - 滚动位置(scrollTop):列表滚动的距离。
- 每项高度(itemHeight):如果固定高度(如
50px
),直接计算;如果动态高度,需测量并缓存。
2. 动态计算要渲染的数据
起始索引(startIndex)
startIndex = Math.floor(scrollTop / itemHeight)
结束索引(endIndex)
endIndex = startIndex + Math.ceil(viewportHeight / itemHeight)
可见数据(visibleData)
visibleData = data.slice(startIndex, endIndex + bufferSize)
(bufferSize
是缓冲区,比如上下多渲染 5 项,避免快速滚动时白屏)
3. 模拟完整列表的滚动条
- 占位元素:设置一个高度为
总数据量 × 每项高度
的空元素,撑开滚动容器。<div style="height: totalHeight;"></div>
- 内容偏移:用
transform: translateY(startIndex * itemHeight)
将可视数据定位到正确位置。
三、示例
简单代码示例(Vue 3 固定高度)
<template>
<div
class="viewport"
@scroll="handleScroll"
:style="{ height: viewportHeight + 'px' }"
>
<!-- 占位元素,撑开滚动条 -->
<div :style="{ height: totalHeight + 'px' }"></div>
<!-- 实际渲染的内容 -->
<div
class="visible-items"
:style="{ transform: `translateY(${offsetY}px)` }"
>
<div
v-for="item in visibleItems"
:key="item.id"
class="item"
:style="{ height: itemHeight + 'px' }"
>
{{ item.text }}
</div>
</div>
</div>
</template>
<script>
import { ref, computed } from "vue";
export default {
setup() {
const viewportHeight = 500; // 可视区域高度
const itemHeight = 50; // 每项固定高度
const scrollTop = ref(0); // 当前滚动位置
// 模拟数据(10,000 条)
const data = Array.from({ length: 10000 }, (_, i) => ({
id: i,
text: `Item ${i + 1}`,
}));
// 计算总高度
const totalHeight = computed(() => data.length * itemHeight);
// 计算起始索引
const startIndex = computed(() => Math.floor(scrollTop.value / itemHeight));
// 计算可见项数量
const visibleCount = computed(() => Math.ceil(viewportHeight / itemHeight));
// 获取可见数据(+5 缓冲)
const visibleItems = computed(() =>
data.slice(startIndex.value, startIndex.value + visibleCount.value + 5)
);
// 计算偏移量
const offsetY = computed(() => startIndex.value * itemHeight);
// 处理滚动事件
const handleScroll = (e) => {
scrollTop.value = e.target.scrollTop;
};
return { data, totalHeight, visibleItems, offsetY, handleScroll };
},
};
</script>
<style>
.viewport {
overflow: auto;
position: relative;
border: 1px solid #eee;
}
.visible-items {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.item {
padding: 10px;
border-bottom: 1px solid #ddd;
}
</style>
动态高度虚拟列表的额外处理
如果列表项高度不固定:
- 维护位置缓存:存储每项的
top
、bottom
、height
。 - 渲染后测量实际高度:用
getBoundingClientRect()
或小程序uni.createSelectorQuery()
。 - 二分查找起始索引:根据
scrollTop
快速定位startIndex
。
总结
场景 | 实现要点 | 复杂度 |
---|---|---|
固定高度 | 直接计算 startIndex 和 offsetY | 低 |
动态高度 | 需维护位置缓存 + 二分查找 | 高 |
优化手段 | 缓冲区、DOM 复用、防抖 | - |
推荐库:
- Vue:
vue-virtual-scroller
- React:
react-window
- UniApp:
z-paging
更新日志
2025/8/24 08:17
查看所有更新日志
e7112
-1于