手写一个虚拟表格--仿vxe-table

最近项目中用到了[vxe-table](vxe-table v4 (vxetable.cn))的虚拟表格组件,出于好奇,就仿照手写了一个。效果呈现:

实现原理

虚拟列表主要作用是只渲染可视范围内的DOM元素,通过滚动条的滚动去替换数据,达到数据滚动切换的效果。比如表格需要展示1000条数据,每行高度为50px(只考虑高度一致的情况),可视区域高度500px,那实际渲染的表格行节点只有十个。所以使用虚拟列表可以有效减少渲染的DOM节点。

实现方式

主要结构由外层占位元素、撑开滚动条的元素、实际内容展示元素三部分构成,需要有一个div去撑开滚动条的高度,这个元素的高度就是行高乘以数据总数,实际内容相对于占位元素定位,top等于滚动的高度,让内容始终在可视区域中。

实现代码

vxe-table的表格组件由表头和内容两部分组成,我也参照了这个实现方式。

表头部分

columns为列配置数组,autoColWidth是列的默认宽度,配置项中没有设置宽度,会使用默认宽度。

{{ col.title }}

const columns = ref([
{ field: ‘id’,title:‘id’, width: 200},
{ field: “name”, title: “name”, width: 200 },
{ field: “sex”, title: “sex” },
{ field: “role”, title: “role” },
])

const autoColWidth = ref(0)
const scrollBarWidth = ref(20) //滚动条宽度
const getAutoColWidth = () => {
let count = 0
const width = columns.value.reduce((width,item) => {
if(item.width){
width = width + item.width
}else{
count++
}
return width
},0)
return (vxeTableRef.value?.clientWidth as number - width - scrollBarWidth.value)/count
}

内容部分

{{ data[col.field] }}

interface TableType {
id: number;
name: string;
sex: string;
role: string;
}

let tableData:TableType =
const filterData = ref<tabletype>()
const count = 10 //展示的行数
const rowHeight = 50 //行高
setTimeout(() => {
// 模拟数据
const mockList =
for (let index = 0; index < 500; index++) {
mockList.push({
id: index,
name: ‘Test’ + index,
role: ‘Developer’,
sex: ‘男’
})
}
tableData = mockList
filterData.value = tableData.slice(0,count)
}, 100)

let scrollHeight = 0
let scrollStart = 0
let scrollEnd = count
const handleScroll = (e:any) => {
scrollHeight = e.target?.scrollTop
scrollStart = Math.floor(e.target?.scrollTop/rowHeight)
scrollEnd = scrollStart + count
filterData.value = tableData.slice(scrollStart,scrollEnd)
}
</tabletype>

给内容div绑定滚动事件,滚动时去计算该滚动距离时表格数据的下标,截取数据,重新渲染表格。

结语

[源码地址](src/components/vxeTable/index.vue · wuyin/vue3+ts+pinia后台模板 - 码云 - 开源中国 (gitee.com)),用vue3写的,就懒得从项目里抽离了。 项目里还实现了列宽的移动设置,有兴趣的可以自行查看源码。


这是一个从 https://juejin.cn/post/7368759192256217151 下的原始话题分离的讨论话题