🐳 引言
上一篇文章我们讲了虚拟列表的实现原理,以及在vue3中的使用实例,感兴趣的友友们可以查看相关内容 传送门。然而,在实际开发中,我们可能会面临其他需求,例如在 el-table
中无法使用分页技术的情况下展示海量数据。这种情况下,页面可能会出现卡顿,严重时甚至可能引发浏览器崩溃。针对这一问题,我们需要寻找其他的解决方案。
🐳 创建virtualTable组件
首先新建一个virtualTable.vue
文件,代码如下:
virtualTable.vue
<template>
<div style="width: 100%">
<el-table v-virtual-list="virtual" width="100%" height="600" :data="tableData.slice(start, over)">
<el-table-column align="center" prop="date" label="日期" :height="virtual.itemHeight" min-width="180"></el-table-column>
<el-table-column align="center" prop="name" label="姓名" :height="virtual.itemHeight" min-width="180"></el-table-column>
<el-table-column align="center" prop="address" label="地址" :height="virtual.itemHeight" min-width="180"></el-table-column>
</el-table>
</div>
</template>
<script>
let timeout = null
export default {
name: 'virtualTable',
data() {
return {
tableData: [],
virtual: {
itemHeight: 40, // 每一项高度
scrollClass: '.el-table__body-wrapper', // 有滚动条元素
scrollTop: 0, // 滚动条距离顶部距离
tableHeight: 0 // 可视区高度
}
}
},
computed: {
// 起始下标
start() {
return Math.max(Math.ceil(this.virtual.scrollTop / this.virtual.itemHeight - 5), 0)
},
// 结束下标
over() {
return Math.min(
Math.ceil((this.virtual.scrollTop + this.virtual.tableHeight + 1) / this.virtual.itemHeight + 5),
this.tableData.length
)
},
// 为了保持列表高度完整且滚动条能正常滚
paddingAttr() {
let bottom = (this.tableData.length - this.over) * this.virtual.itemHeight
let top = this.start * this.virtual.itemHeight
return `${top}px 0 ${bottom}px 0`
}
},
mounted() {
this.init()
},
methods: {
init() {
let _arr = []
for (let i = 1; i <= 100000; i++) {
_arr.push({
date: '2016-05-02',
name: '王小虎' + i,
address: `上海市普陀区金沙江路 ${i} 弄`
})
}
this.tableData = _arr
timeout = setTimeout(() => {
const target = document.querySelector(this.virtual.scrollClass)
this.virtual.tableHeight = target.clientHeight ?? 0
this.setPaddingAttr(target)
})
},
// 更新padding属性
setPaddingAttr(target) {
let _table = target.querySelector('table')
_table.style.padding = this.paddingAttr
}
},
beforeDestroy() {
clearTimeout(timeout)
}
}
</script>
<style scoped>
::v-deep th,
::v-deep td {
height: 40px;
line-height: 40px;
padding: 0;
}
</style>
代码介绍
这边重点说一下里面的关键代码,具体原理可以参考我上一篇文章 传送门
virtual
对象
virtual
对象虚拟列表相关的属性,放在一个对象中方便管理,需要传入到自定义指令中。
timeout = setTimeout(() => {})
为了解决组件初始化时nextTick
中无法获取到正确的clientHeight
。
this.virtual.tableHeight = target.clientHeight ?? 0
组件初始化时获取容器的可视区高度。
this.setPaddingAttr(target)
设置paddingAttr
方法,重新计算padding
,为了确保虚拟列表的渲染效果正确。
tips
当然这里我们可以将其封装成一个公共的组件,如果有相同的需求直接使用当前组件。这里我就不去封装了,感兴趣的友友们可以去尝试一下。
🐳 添加自定义指令
directives.js
// 虚拟列表自定义指令
Vue.directive('virtual-list', {
bind(el, bind, vNode) {
const that = vNode.context
let virtual = bind.value
if (virtual) {
let target = el.querySelector(virtual.scrollClass)
target.addEventListener('scroll', () => {
// 更新滚动条位置
that.virtual.scrollTop = target.scrollTop
// 重新计算padding
that.setPaddingAttr(target)
})
}
}
})
关键代码介绍
const taht = vNode.context
获取当前指令所在组件的上下文this
,以便在后续的代码中访问组件的方法和数据。
let virtual = bind.value
获取组件中传入的值,内部包含虚拟列表的相关信息。
target.addEventListener('scroll', () => {...})
给容器添加滚动监听事件,当用户滚动时触发该事件。
that.virtual.scrollTop = target.scrollTop
更新组件中的 virtual.scrollTop
属性,记录当前滚动条的位置。
that.setPaddingAttr(target)
该方法由当前指令所在组件内部提供。重新计算padding
,为了确保虚拟列表的渲染效果正确。
最终效果如下:
tips
该自定义指令不仅可以在 el-table
中实现虚拟列表,还可以在 el-select
等组件中使用。在实际开发中,只要了解其原理,就可以有效地应用于实际操作中。
🐳 文章小尾巴
感谢你看到最后,最后再说两点~
①如果你持有不同的看法,欢迎你在文章下方进行留言、评论。
②如果对你有帮助,或者你认可的话,欢迎给个小点赞,支持一下~
我是程序员张张,一个热爱编程也爱生活的程序员
(文章内容仅供学习参考,如有侵权,非常抱歉,请立即联系作者删除。)