当前位置:首页 > VUE

vue虚拟滚动列表实现

2026-01-20 03:20:10VUE

虚拟滚动列表的实现原理

虚拟滚动(Virtual Scrolling)通过仅渲染可视区域内的元素来优化长列表性能。核心思想是根据滚动位置动态计算可见项,减少DOM节点数量。

基于vue-virtual-scroll-list的实现

安装依赖库:

npm install vue-virtual-scroll-list --save

基础用法示例:

<template>
  <virtual-list 
    :size="50"
    :remain="8"
    :items="items"
  >
    <template v-slot="{ item }">
      <div class="item">{{ item.content }}</div>
    </template>
  </virtual-list>
</template>

<script>
import VirtualList from 'vue-virtual-scroll-list'

export default {
  components: { VirtualList },
  data() {
    return {
      items: Array(10000).fill().map((_, i) => ({
        id: i,
        content: `Item ${i}`
      }))
    }
  }
}
</script>

<style>
.item {
  height: 50px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
}
</style>

自定义实现方案

手动实现的基本逻辑:

vue虚拟滚动列表实现

<template>
  <div 
    class="viewport" 
    @scroll="handleScroll"
    ref="viewport"
  >
    <div 
      class="scroll-space" 
      :style="{ height: totalHeight + 'px' }"
    >
      <div 
        class="visible-items"
        :style="{ transform: `translateY(${offset}px)` }"
      >
        <div 
          v-for="item in visibleItems" 
          :key="item.id"
          class="item"
          :style="{ height: itemHeight + 'px' }"
        >
          {{ item.content }}
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    items: Array,
    itemHeight: {
      type: Number,
      default: 50
    },
    visibleCount: {
      type: Number,
      default: 10
    }
  },
  data() {
    return {
      startIndex: 0,
      offset: 0
    }
  },
  computed: {
    totalHeight() {
      return this.items.length * this.itemHeight
    },
    endIndex() {
      return Math.min(
        this.startIndex + this.visibleCount, 
        this.items.length
      )
    },
    visibleItems() {
      return this.items.slice(
        this.startIndex, 
        this.endIndex
      )
    }
  },
  methods: {
    handleScroll() {
      const scrollTop = this.$refs.viewport.scrollTop
      this.startIndex = Math.floor(scrollTop / this.itemHeight)
      this.offset = this.startIndex * this.itemHeight
    }
  }
}
</script>

<style>
.viewport {
  height: 500px;
  overflow-y: auto;
  position: relative;
}
.scroll-space {
  position: relative;
}
.visible-items {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}
</style>

性能优化技巧

设置适当的key属性避免不必要的重渲染,使用稳定的唯一标识符而非数组索引

添加滚动节流处理:

import { throttle } from 'lodash'

methods: {
  handleScroll: throttle(function() {
    // 计算逻辑
  }, 16)
}

对于动态高度项目,需要实现尺寸测量和位置缓存。可以使用ResizeObserver API监测元素实际高度并更新位置计算

vue虚拟滚动列表实现

动态高度处理方案

安装依赖:

npm install vue-virtual-scroller

动态高度示例:

<template>
  <RecycleScroller
    class="scroller"
    :items="items"
    :item-size="32"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="dynamic-item">
      {{ item.text }}
    </div>
  </RecycleScroller>
</template>

<script>
import { RecycleScroller } from 'vue-virtual-scroller'

export default {
  components: { RecycleScroller },
  data() {
    return {
      items: generateDynamicItems()
    }
  }
}
</script>

<style>
.scroller {
  height: 400px;
}
.dynamic-item {
  padding: 10px;
}
</style>

注意事项

滚动容器需要明确设置高度,否则无法正确计算可视区域。对于服务端渲染(SSR)场景,需要特殊处理初始渲染

大数据量情况下,建议配合分页加载策略。可通过Intersection Observer API实现滚动到底部自动加载更多数据

标签: 列表vue
分享给朋友:

相关文章

vue 页签实现

vue 页签实现

Vue 页签实现方法 在 Vue 中实现页签功能通常涉及动态组件、路由或状态管理。以下是几种常见的实现方式: 使用动态组件 通过 Vue 的 <component> 动态组件结合 v-f…

vue搜索过后实现分页

vue搜索过后实现分页

Vue 实现搜索后分页功能 数据绑定与搜索逻辑 在 Vue 组件中定义必要的数据属性: data() { return { searchQuery: '', currentPa…

vue使用vr实现标注

vue使用vr实现标注

Vue 中使用 VR 实现标注的方法 在 Vue 项目中结合 VR 技术实现标注功能,可以通过 WebXR 和 Three.js 等库实现。以下为具体实现方法: 安装依赖 确保项目中已安装 Thr…

vue自己实现下拉导航

vue自己实现下拉导航

实现下拉导航的基本思路 在Vue中实现下拉导航,可以通过结合Vue的响应式数据和事件绑定特性来完成。主要思路是利用v-show或v-if控制下拉菜单的显示与隐藏,并通过鼠标事件或点击事件触发状态变化。…

vue实现画圆弧并着色

vue实现画圆弧并着色

在 Vue 中实现画圆弧并着色 使用 Canvas API 绘制圆弧 在 Vue 组件的 mounted 钩子中,通过 Canvas API 绘制圆弧。创建一个 canvas 元素并获取其上下文:…

vue 路由 实现

vue 路由 实现

Vue 路由实现方法 Vue Router 是 Vue.js 的官方路由管理器,用于构建单页面应用(SPA)。以下是实现 Vue 路由的详细步骤: 安装 Vue Router 通过 npm 或 ya…