在 vue 中手写虚拟列表

444 阅读1分钟

后端一次返回数据过多,或者向下滚动过程会叠加渲染产生的新数据,会导致页面的 dom 元素越来越多,慢慢也就会变的有卡顿的感觉,于是想到使用虚拟列表。只渲染用户看到的数据,操作过程中不停更新用户看到的数据。

1.定义变量

data() {
 return {
  fleetAll: [],  //  列表全部数据
  fleetShowList: [],  //  列表全部数据中需要的渲染数据
  fleetScrollTop: 0,  //  父级 y 轴 scroll 滚动距离
  fleetBoxHeight: 12*32,  //  父级元素的高度  默认渲染12条
 }
},

2.template 模板

<div class="h300 pr ova" @scroll="scrollFleet">
 <div :style="'height: '+fleetBoxHeight+'px;'">
  <div class="lin32 hover pa l0" :style="'top: '+item.top+'px;'" v-for="(item, keys) in fleetShowList" :key="item.id">{{ item.name }}</div>
 </div>
</div>

3. 函数处理

//  初始化数据
initData() {
 let fleetList = []
 for (let i=1;i<200;i++) {
  fleetList.push({id: String(i), name: '车队'+String(i).padStart(3, '0')+'车队'})
 }
 this.fleetBoxHeight = fleetList.length*32
 this.fleetAll = fleetList
},
//  scroll 事件处理
scrollFleet(e) {
 this.fleetScrollTop = e.target.scrollTop
 this.handleFleetShowList()
},
//  scroll 过程中渲染数据处理
handleFleetShowList() {
 let oldStartIdx = this.startIdx
 //  防止滚动到最下面时如果滚动距离超过最大可滚动距离,会出现最后一个元素定位超出,导致一直可以滚动
 if (this.fleetBoxHeight>300 && this.fleetScrollTop>(this.fleetBoxHeight-300)) {
  this.fleetScrollTop = (this.fleetBoxHeight-300)
 }
 this.startIdx = Math.floor(this.fleetScrollTop / 32)
 let fleet12 = []
 let fl = fleet12.length
 while (fl<12 && (this.startIdx+fl)<this.fleetAll.length) {
  let fitem = this.fleetAll[this.startIdx+fl]
  if (oldStartIdx != this.startIdx) {
   fitem.top = this.fleetScrollTop+(fl*32)
  }
  //  向上滚动到第一条数据时,可能存在第一条数据有定位距离,如果接着向上滚动,则清空第一条定位距离
  if (oldStartIdx == 0 && this.startIdx == 0) {
   fitem.top = fl*32
  }
  fleet12.push(fitem)
  fl = fleet12.length
 }
 this.fleetShowList = fleet12
},

4.虚拟列表效果

虚拟列表.gif