虚拟列表、虚拟滚动 
虚拟列表是一种优化长列表渲染性能的技术,通过仅渲染可视区域内的元素,减少 DOM 节点数量和渲染开销。以下是其实现的核心原理与步骤:
1. 核心原理 
- 视窗渲染:只渲染用户视野内的元素,动态替换滚动时进入视野的元素。
- 滚动占位:用空白区域(或占位符)模拟完整列表的滚动长度。
- 动态计算:根据滚动位置和元素尺寸,计算当前应渲染的元素范围。
- 定位偏移:通过 transform或absolute定位,让元素出现在正确位置。
2. 实现步骤(以固定高度为例) 
(1) HTML 结构设计 
html
<!-- 容器负责滚动,高度由外部定义 -->
<div class="virtual-container">
  <!-- 总高度撑起滚动条 -->
  <div class="scroll-area" :style="{ height: totalHeight + 'px' }">
    <!-- 可视区域,绝对定位渲染当前项 -->
    <div class="visible-items" :style="{ transform: `translateY(${offset}px)` }">
      <div v-for="item in visibleItems" :key="item.id" class="item">{{ item.text }}</div>
    </div>
  </div>
</div>1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
(2) CSS 样式 
css
.virtual-container {
  overflow-y: auto; /* 允许滚动 */
  height: 600px; /* 容器可视高度 */
}
.scroll-area {
  position: relative; /* 相对定位,用于子元素定位 */
}
.visible-items {
  position: absolute; /* 绝对定位,根据滚动偏移计算位置 */
  top: 0;
  left: 0;
  width: 100%;
}
.item {
  height: 50px; /* 固定高度项 */
  border-bottom: 1px solid #eee;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(3) JavaScript 逻辑 
javascript
new Vue({
  data: {
    items: [], // 所有数据(假设1000条)
    itemHeight: 50, // 每项固定高度
    visibleCount: 0, // 可视区能显示多少项
    startIndex: 0, // 起始索引
    offset: 0 // Y轴偏移量
  },
  computed: {
    // 可渲染的总高度(撑起滚动条)
    totalHeight() {
      return this.items.length * this.itemHeight;
    },
    // 当前可视区域的数据切片
    visibleItems() {
      return this.items.slice(
        this.startIndex,
        this.startIndex + this.visibleCount
      );
    }
  },
  mounted() {
    // 初始化可视区能容纳的项数
    this.visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight);
    // 监听滚动事件
    this.$el.addEventListener('scroll', this.handleScroll);
  },
  methods: {
    handleScroll(e) {
      const scrollTop = e.target.scrollTop;
      // 计算当前起始索引(被卷走了多少个项)
      this.startIndex = Math.floor(scrollTop / this.itemHeight);
      // 计算偏移量(让元素出现在正确位置)
      this.offset = this.startIndex * this.itemHeight;
    }
  },
  beforeDestroy() {
    this.$el.removeEventListener('scroll', this.handleScroll);
  }
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
3. 动态高度优化 
若列表项高度不固定,需 实时测量 + 缓存结果,算法复杂度更高:
(1) 预估初始高度 
javascript
// 假设初始预估高度为 100px
estimatedHeight: 100,
// 总高度(基于预估值)
totalHeight() {
  return this.items.length * this.estimatedHeight;
}1
2
3
4
5
6
2
3
4
5
6
(2) 监听项的实际高度 
javascript
// 记录每个项的实际高度
heights: [],
// 更新高度缓存
updateHeight(index, height) {
  this.$set(this.heights, index, height);
  // 重新计算总高度(实际值)
  this.totalHeight = this.heights.reduce((sum, h) => sum + (h || this.estimatedHeight), 0);
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
(3) 根据实际高度计算偏移 
需维护一个 位置数组(每个项的累计高度),如:
javascript
// positions存储每个项的起始位置和高度
positions.push({
  top: prevItemTop + prevItemHeight,
  height: currentItemHeight,
  bottom: prevItemTop + prevItemHeight + currentItemHeight
});1
2
3
4
5
6
2
3
4
5
6
(4) 二分查找当前滚动位置对应的索引 
根据 scrollTop 快速查找起始索引:
javascript
function findStartIndex(scrollTop) {
  let low = 0, high = positions.length;
  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    if (positions[mid].bottom < scrollTop) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }
  return low;
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
4. 性能优化技巧 
- 缓冲渲染:多渲染几个屏幕外的元素(如上 / 下各多 5 个),避免滚动白屏。javascript// 计算时增加缓冲区 startIndex = Math.max(0, startIndex - buffer); endIndex = Math.min(items.length - 1, endIndex + buffer);1
 2
 3
- 节流滚动事件:限制 scroll触发频率,减少计算次数。javascripthandleScroll: throttle(function(e) { /* ... */ }, 50)1
- 使用 transform替代top:transform不触发重排,性能更好。
5. 适用场景与工具库 
- 适用:长列表(如聊天记录、表格数据、社交动态流)。
- 工具库: - React: react-window、react-virtualized
- Vue: vue-virtual-scroller、vue-virtual-scroll-list
 
- React: 
通过虚拟列表,可大幅提升长列表的渲染性能与用户体验!
举例:Vue 虚拟滚动组件实现