vue + el-table 虚拟列表的实现
el-table 虚拟列表的实现
不到万不得已不建议使用虚拟列表,建议使用分页和过滤器。
但有些时候事与愿违,功能就必须要把列表全展示下来,列表多达上万条数据,并且字段高达100,这样下来接口访问10s以上,就更别说页面渲染的压力了,没办法只能用虚拟列表吧,废话不多说,直接开干。
1.原理,借用网图
思路也比较明了,一共有n条数据,但我们只渲染可视区域的局部数据,通过滚动列表从而得知当前滚动的距离,计算出开始索引,从而获取新的展示区域的局部数据。
2.准备条件
开始索引:start,用于总数据切片头部
结束索引:end ,用于总数据切片尾部
每行表格行高:ItemHeight,(这边需求是等高的列表)
可视区域的Item数量:num ,(最多展示多少行)
3.开始实现
<div ref="listWrap" style="height:100px;overflow-y: scroll;" @scroll="scrollListener">
<div ref="list">
<el-table ref="scrollTable" v-loading="tableLoading" :data="showList">
...
</el-table>
</div>
</div>
....
data(){
return {
tableData:[], // 总共的列表数据
itemHeight: 40, // item高度
num: 10, // 展示的数据
start: 0, // 开始索引
end: 9 // 结束索引
}
}
mounted() {
this.$refs.listWrap.style.height = '100px' // 设置可视区域的高度
},
watch:{
length(val){
// 超过10行数据,就按照最大40*10 400px高度的列表就行
if (val >= 10) {
this.$refs.listWrap.style.height = this.itemHeight * this.num+ 'px'
} else {
// 不足10行数据,这边 加57是因为表头的高度,具体情况,你们加不加无所谓
this.$refs.listWrap.style.height = this.itemHeight * val + 57 + 'px'
}
}
},
computed: {
// 获取展示的列表
showList() {
return this.tableData.slice(this.start, this.end)
},
// 获取列表长度
length() {
return this.tableData.length || 0
}
},
methods: {
scrollListener() {
// 获取滚动高度
const scrollTop = this.$refs.listWrap.scrollTop
// 开始的数组索引
this.start = Math.floor(scrollTop / this.itemHeight)
// 结束索引
this.end = this.start + this.num
this.$refs.list.style.transform = `translateY(${this.start * this.itemHeight}px)`// 对列表项y轴偏移
},
// ajax获取 tableData
getData(){
xxx().then(()=>{
....
this.tableData = xxx
}).catch()
}
}
设置初始盒子高度100px,然后获得总数据之后,通过计算属性计算出展示的数据数量不足10个按照个数计算展示高度即可,超过10个最多也是最大高度,我这边是400
这样简单的一个虚拟列表完善了,当然几千条数据 就算每行100个字段也不会这么卡,因为我这边还有好多输入框嵌套在表格中,不免会卡顿
4. 继续优化
当数据量过多的时候,用户需要拉到右侧去操作其他数据,这时候需要滑动到底部再看很影响用户体验,可以监听一个滚动条和el-table的横向滚动条滚动距离一致,然后将el-table自带的overflow-x:hidden !important,注意要加层级,这样可以增加用户体验
<div ref="listWrap" style="height:100px;overflow-y: scroll;" @scroll="scrollListener">
<div ref="list">
<el-table ref="scrollTable" v-loading="tableLoading" :data="showList">
...
</el-table>
</div>
</div>
<div ref="bottomScroll" class="bottom-scroll">
<div class="bottom-scroll-content" :style="{ width: bottomScrollWidth }" />
</div>
...
watch:{
bottomScrollWidth: {
// 两个滚动条同时滚动
handler(newVal, oldVal) {
// 监听滚动条
this.$nextTick(() => {
this.tableDom = this.$refs.scrollTable.bodyWrapper
this.tableDom.addEventListener('scroll', () => {
// 与表格滚动条同步
const scrollLeft = this.tableDom.scrollLeft
this.$refs.bottomScroll.scrollTo(scrollLeft, 0)
})
const bottomScroll= this.$refs.bottomScroll
bottomScroll.addEventListener('scroll', () => {
// 与表格滚动条同步
const scrollLeft = this.$refs.bottomScroll.scrollLeft
this.$refs.scrollTable.bodyWrapper.scrollTo(scrollLeft, 0)
})
})
},
deep: true
}
.... scoped
.bottom-scroll {
width: 100%;
overflow-x: auto;
}
.bottom-scroll-content {
/* 高度不设置的话滚动条出不来 */
height: 1px;
}
// 让该页面的横向滚动条隐藏
/deep/.el-table--scrollable-x .el-table__body-wrapper{
overflow-x: hidden !important;
}
...
总代码如下
<div ref="listWrap" style="height:100px;overflow-y: scroll;" @scroll="scrollListener">
<div ref="list">
<el-table ref="scrollTable" v-loading="tableLoading" :data="showList">
...
</el-table>
</div>
</div>
<div ref="bottomScroll" class="bottom-scroll">
<div class="bottom-scroll-content" :style="{ width: bottomScrollWidth }" />
</div>
....
data(){
return {
tableData:[], // 总共的列表数据
itemHeight: 40, // item高度
num: 10, // 展示的数据
start: 0, // 开始索引
end: 9 // 结束索引
}
}
mounted() {
this.$refs.listWrap.style.height = '100px' // 设置可视区域的高度
},
watch:{
length(val){
// 超过10行数据,就按照最大40*10 400px高度的列表就行
if (val >= 10) {
this.$refs.listWrap.style.height = this.itemHeight * this.num+ 'px'
} else {
// 不足10行数据,这边 加57是因为表头的高度,具体情况,你们加不加无所谓
this.$refs.listWrap.style.height = this.itemHeight * val + 57 + 'px'
}
},
bottomScrollWidth: {
// 两个滚动条同时滚动
handler(newVal, oldVal) {
// 监听滚动条
this.$nextTick(() => {
this.tableDom = this.$refs.scrollTable.bodyWrapper
this.tableDom.addEventListener('scroll', () => {
// 与表格滚动条同步
const scrollLeft = this.tableDom.scrollLeft
this.$refs.bottomScroll.scrollTo(scrollLeft, 0)
})
const bottomScroll= this.$refs.bottomScroll
bottomScroll.addEventListener('scroll', () => {
// 与表格滚动条同步
const scrollLeft = this.$refs.bottomScroll.scrollLeft
this.$refs.scrollTable.bodyWrapper.scrollTo(scrollLeft, 0)
})
})
},
deep: true
}
},
computed: {
// 获取展示的列表
showList() {
return this.tableData.slice(this.start, this.end)
},
// 获取列表长度
length() {
return this.tableData.length || 0
}
},
methods: {
scrollListener() {
// 获取滚动高度
const scrollTop = this.$refs.listWrap.scrollTop
// 开始的数组索引
this.start = Math.floor(scrollTop / this.itemHeight)
// 结束索引
this.end = this.start + this.num
this.$refs.list.style.transform = `translateY(${this.start * this.itemHeight}px)`// 对列表项y轴偏移
},
// ajax获取 tableData
getData(){
xxx().then(()=>{
....
this.tableData = xxx
}).catch()
}
}
.... scoped
.bottom-scroll {
width: 100%;
overflow-x: auto;
}
.bottom-scroll-content {
/* 高度不设置的话滚动条出不来 */
height: 1px;
}
// 让该页面的横向滚动条隐藏
/deep/.el-table--scrollable-x .el-table__body-wrapper{
overflow-x: hidden !important;
}
...
效果如下
这样可以实现多数据量也不会卡顿的情况,至于后端需要查询10多秒,就让后端优化sql什么的,但注意的是,一旦用虚拟列表,索引将不复存在,无法通过索引作为使用条件,因为可视区域下都是临时虚拟索引,一旦滚动条滚动,索引将毫无意义。
更多推荐
所有评论(0)