使用padding实现表格节点的虚拟滚动
代码示例为总数量500条,每次只渲染当前展示区域内的10条
直接粘完整代码
<template>
<div class="new-component">
<el-table
ref="table"
v-loading="listLoading"
class="virtual-table"
:data="virtualRows"
:max-height="440"
:row-key="getRowKeys"
@select-all="handleSelectionAll"
>
<el-table-column type="selection" :reserve-selection="true" width="55" />
<el-table-column prop="date" label="日期" min-width="180px" />
<el-table-column prop="name" label="姓名" min-width="180px" />
<el-table-column prop="address" label="地址" min-width="180px" />
</el-table>
</div>
</template>
<script>
import { debounce } from 'throttle-debounce'
export default {
data() {
return {
tableData: [],
virtualRows: [],
listLoading: false,
selectedAllStatus: false,
selectedRow: [],
selectedRowIds: []
}
},
created() {
for (let i = 0; i < 100; i++) {
this.tableData.push({
id: i,
date: i,
name: 'i' + i,
address: i + '-' + i + '-' + i + '-' + i
})
}
this.throttleResize = debounce(100, () => {
this.handleScroll()
})
},
beforeMount() {},
mounted() {
this.addListeners()
this.updateVisibleItems()
},
beforeDestroy() {
this.removeListeners()
},
methods: {
// 对table增加监听事件
addListeners() {
if (!this.$refs.table.bodyWrapper) {
return
}
this.$refs.table.bodyWrapper.addEventListener('scroll', this.throttleResize)
},
removeListeners() {
if (!this.$refs.table.bodyWrapper) {
return
}
this.$refs.table.bodyWrapper.removeEventListener('scroll', this.throttleResize)
},
handleScroll() {
console.count('处理滚动事件')
this.updateVisibleItems()
},
updateVisibleItems() {
const itemSize = 40 // td高度
const count = this.tableData.length // table总条数
const bufferCount = 10 // table每次实际渲染条数
const height = count * itemSize
// 获取当前滚动条位置
const scroll = this.getScroll()
const scrollTop = scroll.start
// Fixed size mode
let startIndex
startIndex = ~~(scrollTop / itemSize)
startIndex < 0 && (startIndex = 0)
// 分隔数组
this.virtualRows = this.tableData.slice(startIndex, startIndex + bufferCount)
// 设置el-table上的虚拟列表,采用了padding的方案,原因是transform 会使el-table的样式混乱
const mainTable = this.$refs.table.$el.getElementsByClassName('el-table__body')
Array.from(mainTable).forEach(table => {
table.style.height = height + 'px'
if (startIndex + bufferCount >= count) {
// 由于el-table 在滚动到最后时,会出现抖动,因此增加判断,单独设置属性
table.style.paddingTop = scrollTop - itemSize + 'px'
table.style.paddingBottom = 0
} else {
table.style.paddingTop = scrollTop + 'px'
table.style.paddingBottom = height - scrollTop - bufferCount * itemSize + 'px'
}
})
// 切换选中状态
},
getScroll() {
if (!this.$refs.table.bodyWrapper) {
return {
start: 0,
end: 400
}
}
const dom = this.$refs.table.bodyWrapper
const scrollState = {
start: dom.scrollTop,
end: dom.scrollTop + dom.clientHeight
}
return scrollState
},
handleSelectionAll(selection) {
this.selectedAllStatus = !this.selectedAllStatus
const refsElTable = this.$refs.table // 获取表格对象
const tableDataLength = this.tableData.length
if (this.selectedAllStatus) {
console.log('选中所有行')
refsElTable.clearSelection() // 先清空
for (let index = 0; index < tableDataLength; index++) {
// 选中所有行
refsElTable.toggleRowSelection(this.tableData[index], true)
}
} else {
console.log('清空选中所有行')
refsElTable.clearSelection()
}
},
getRowKeys(row) {
return row.id // 设定每一条对应一个key
}
}
}
</script>
<style scoped>
::v-deep.el-table th {
background: #f4f4f4 !important;
height: 40px !important;
padding: 0 0 !important;
}
::v-deep.el-table td {
height: 40px !important;
padding: 0 0 !important;
}
</style>