看了珠峰老师的课,实现了一下简单的
vue-virtualList
介绍
什么是 vue-virtualList
是在运行在 vue 项目中,为了解决 数据量 过大,导致渲染 DOM 数量过多,影响浏览器性能,为了解决这个问题,出现的一个组件
使用
<VirtualList :items="items" :remain="remain" :size="size" :variable="true">
<myItem slot-scope="{item}" :item="item" />
</VirtualList>
参数含义
| items | remain | size | variable |
|---|---|---|---|
| 数据来源 | 可见的数量 | 每个item 的高度 | 是否高度不确定 |
原理分析
基本原理
主要是利用了 对数据的不断截取,只渲染截取的数据
graph TD
Start --> 传递数据
传递数据-->确定数据截取的起始值
确定数据截取的起始值-->截取数据
发生滚动--> 更改截取数据的起始值
更改截取数据的起始值-->截取数据
截取数据-->渲染页面
html 结构
<div class="viewport" ref="viewport" @scroll="handleScroll">
<!-- 滚动条 -->
<div class="scroll-bar" ref="scrollBar"></div>
<!-- 真实位置 -->
<div class="scroll-list" :style="{top:this.offset+'px'}">
<div v-for="(item,index) in visiableData" :index="item.id" :vid="item.id" ref="items">
<slot :item="item"/>
</div>
</div>
</div>
css
.viewport {
overflow-y: scroll;
position: relative;
}
.scroll-list {
position: absolute;
top: 0;
width: 100%;
}
- 截取数据
// 起始的 start 为0,end 是 可见的数量 data(){ start:0, end:this.remain, }, visiableData(){ return this.items.slice(this.start,this.end) }, - 确定滚动条的高度
mounted(){
this.$refs.viewport.style.height = this.remain * this.size + 'px'
// 设置滚动条的高度
this.$refs.scrollBar.style.height =this.items.length * this.size + 'px'
}
这样的话,简单的原理就实现了,当用户滑动的时候,只需要不断的改变 start 和 end 就可以获取最新的数据
- 滚动时候改变 start 和 end 值
获取最外层的滚动高度,高度 / 每一项的高度 就是应该开始的 start 值
handleScroll(){
let scrollTop = this.$refs.viewport.scrollTop;
this.start = Math.floor( scrollTop / this.size )
this.end = this.start + this.remain;
}
到此为止,一个简单的虚拟列表滚动就实现了,但是会有一个小问题,当用户下拉较快的时候,会有白屏的问题,因为每次页面上的数据是刚好是用户所能看到的全部,当下拉的时候,需要 js 宏任务执行,才会执行GUI 渲染,这个需要时间,所以我们要渲染的结果要稍微大于用户看到的结果
优化
在原先的 slice 上添加 prevCount 和 nextCount,扩大渲染数量
添加 prevCount 和 nextCount
computed:{
prevCount(){
return Math.min( this.start,this.remain )
},
nextCount(){
return Math.min( this.end, this.items.length - this.end )
},
visiableData(){
// start 减小, end 值加大
let start = this.start - this.prevCount
let end = this.end + this.nextCount
return this.items.slice(start,end)
}
},
滚动的时候需要减去重复的数据,因为滚动的时候提前了,会有一段时间重复数据
handleScroll(){
this.start = Math.floor( scrollTop / this.size )
this.end = this.start + this.remain;
// 需要把预留出来的偏移量 减去
// 因为滚动的时候 start 提前了,会有一段时间重复数据
this.offset = this.start * this.size - this.prevCount * this.size;
}
**到此为止,基本的结构已经结束了,如果需要使用 variable 不定长的高度的话,比较复杂,以后有时间再说 **