vxe-list vue下拉框的虚拟列表
虚拟列表的实现原理
接下来测试一下
vue虚拟列表实现原理
应用场景
实现思路
基础实现
vxe-list vue下拉框的虚拟列表vxe-table vxe-list vue 实现下拉框的虚拟列表
虚拟列表的实现原理只渲染可视区的 dom 节点,其余不可见的数据卷起来,只会渲染可视区域的 dom 节点,提高渲染性能及流畅性,优点是支持海量数据的渲染;当然也会有缺点:滚动效果相对略差(海量数据与滚动效果的取舍问题就看自己的需求喽);
<div class="my-select">
<input type="text" class="my-select-input" readonly>
<vxe-list class="my-select-wrapper" :loading="loading" :data="list">
<template v-slot="{ items }">
<div class="my-select-option" v-for="item in items" :key="item.value">{{ item.label }}</div>
</template>
</vxe-list>
</div>
export default {
data () {
return {
loading: false,
list: []
}
},
created () {
this.loading = true
setTimeout(() => {
const startTime = Date.now()
var list = []
for(var i=0;i<100000;i++){
list.push({
label: '选项'+i,
value: i
})
}
this.list = list
this.loading = false
this.$nextTick(() => {
this.$XModal.message({ message: `渲染 ${list.length} 行,用时 ${Date.now() - startTime}毫秒`, status: 'info' })
})
}, 200)
}
}
.my-select {
width: 200px;
position: relative;
background-color: #fff;
}
.my-select-input {
width: 100%;
height: 24px;
border: 1px solid #dcdfe6;
}
.my-select-wrapper {
position: absolute;
left: 0;
top: 26px;
width: 100%;
height: 200px;
background-color: #fff;
border: 1px solid #dcdfe6;
}
.my-select-option:hover {
background-color: #f5f7fa;
cursor: pointer;
}
接下来测试一下
渲染 1w 条只需要 150 毫秒左右
渲染 5w 条只需要 300 毫秒左右
渲染 10w 条只需要 500 毫秒左右
具体用法可以去看 官方文档,在线运行 http://jsrun.net/CW2Kp/edit
vue虚拟列表实现原理 应用场景前端的业务开发中会遇到不使用分页方式来加载长列表的需求。如在数据长度大于 1000 条情况,DOM 元素的创建和渲染需要的时间成本很高,完整渲染列表所需要的时间不可接受,同时会存在滚动时卡顿问题;
解决该卡顿问题的重点在于如何降低长列表DOM渲染成本问题,文章将介绍通过虚拟列表渲染的方式解决该问题。
为什么需要虚拟列表
虚拟列表是对长列表的一种优化方案。在前端开发中,会碰到一些不能使用分页方式来加载列表数据的业务形态,我们称这种列表叫做长列表。比如,手机端,淘宝商品展示,美团外卖等,数据量特别庞大,不适合分页,以及懒加载,这时候我们可以采用虚拟列表,只展示可视区域数据。
实现思路虚拟列表的核心思想为可视区域渲染,在页面滚动时对数据进行截取、复用DOM进行展示的渲染方式。
实现虚拟列表就是处理滚动条滚动后的可见区域的变更,其中具体步骤如下:
1.计算当前可见区域起始数据的 startIndex
2.计算当前可见区域结束数据的 endIndex
3.计算当前可见区域的数据,并渲染到页面中
4.计算 startIndex 对应的数据在整个列表中的偏移位置 startOffset,并设置到列表上
基础实现我们首先要考虑的是虚拟列表的 HTML、CSS 如何实现:
列表元素(.list-view)使用相对定位
使用一个不可见元素(.list-view-phantom)撑起这个列表,让列表的滚动条出现
列表的可见元素(.list-view-content)使用绝对定位,left、right、top 设置为 0
html:
<template>
<div
class="list-view"
:style="{
height: `${height}px`
}"
@scroll="handleScroll">
<div
class="list-view-phantom"
:style="{
height: contentHeight
}">
</div>
<ul
ref="content"
class="list-view-content">
<li
class="list-view-item"
:style="{
height: itemHeight + 'px'
}"
v-for="(item, index) in visibleData"
:key="index">
{{ item }}
</li>
</ul>
</div>
</template>
script:
<script>
export default {
name: 'ListView',
props: {
data: {
type: Array,
default: function() {
const list = []
for (let i = 0; i < 1000000; i++) {
list.push('列表' + i)
}
return list
}
},
height: {
type: Number,
default: 400
},
itemHeight: {
type: Number,
default: 30
},
},
computed: {
contentHeight() {
return this.data.length * this.itemHeight + 'px';
}
},
mounted() {
this.updateVisibleData();
},
data() {
return {
visibleData: []
};
},
methods: {
updateVisibleData(scrollTop) {
scrollTop = scrollTop || 0;
const visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight); // 取得可见区域的可见列表项数量
const start = Math.floor(scrollTop / this.itemHeight); // 取得可见区域的起始数据索引
const end = start + visibleCount; // 取得可见区域的结束数据索引
this.visibleData = this.data.slice(start, end); // 计算出可见区域对应的数据,让 Vue.js 更新
this.$refs.content.style.webkitTransform = `translate3d(0, ${ start * this.itemHeight }px, 0)`; // 把可见区域的 top 设置为起始元素在整个列表中的位置(使用 transform 是为了更好的性能)
},
handleScroll() {
const scrollTop = this.$el.scrollTop;
this.updateVisibleData(scrollTop);
}
}
}
</script>
css:
<style lang="scss" scoped>
.list-view {
overflow: auto;
position: relative;
border: 1px solid #aaa;
width: 200px;
}
.list-view-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list-view-content {
left: 0;
right: 0;
top: 0;
position: absolute;
}
.list-view-item {
padding: 5px;
color: #666;
line-height: 30px;
box-sizing: border-box;
}
</style>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持软件开发网。