Skip to content

虚拟列表

vue
<script setup>
import { ref, computed } from 'vue'

// 1. 模拟 10,000 条数据
const items = ref(Array.from({ length: 10000 }, (_, i) => `Item ${i}`))

// 2. 配置参数
const itemHeight = 50 // 每项高度
const visibleCount = 10 // 可见区域显示的最大条数
const containerHeight = itemHeight * visibleCount // 容器高度

// 3. 计算当前显示的范围
const scrollTop = ref(0)
const startIndex = computed(() => Math.floor(scrollTop.value / itemHeight))
const endIndex = computed(() => Math.min(startIndex.value + visibleCount, items.value.length))
const visibleItems = computed(() => items.value.slice(startIndex.value, endIndex.value))
const offsetY = computed(() => startIndex.value * itemHeight)

// 4. 监听滚动
const onScroll = (e) => {
  scrollTop.value = e.target.scrollTop
}
</script>

<template>
  <div class="virtual-list" @scroll="onScroll">
    <!-- 5. 用一个占位容器撑起总高度 -->
    <div class="list-container" :style="{ height: `${items.length * itemHeight}px` }">
      <!-- 6. 只渲染可视区域的元素,并用 transformY 定位 -->
      <div class="list-content" :style="{ transform: `translateY(${offsetY}px)` }">
        <div v-for="(item, index) in visibleItems" :key="startIndex + index" class="list-item">
          {{ item }}
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.virtual-list {
  width: 300px;
  height: v-bind(containerHeight + 'px');
  /* 设置固定高度 */
  overflow-y: auto;
  border: 1px solid #ddd;
  position: relative;
}

.list-container {
  position: relative;
}

.list-content {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

.list-item {
  height: 50px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
  padding-left: 10px;
  background: white;
}
</style>

基于 MIT 许可发布