实现的背景
element ui 的table 没有提供下拉加载后续数据的功能,前端要想优化,要么做分页,要么做数据下拉追加。当数据太多,比如有上千条的时候,一次性渲染出来,会降低首屏加载时间,也会让页面滚动变得卡顿。
实现思路
将table组件的滚动dom找出来,对它做滚动监听,当滚动条快要到底的时候,追加100条数据。追加后,滚动条会上升,等待下一次触发快要到底的时候,追加100条数据。
vue3实现代码
<template>
<!-- 表格 -->
<div class="table-container" ref="tableContainer">
<el-table
:max-height="maxHeight"
:data="renderData"
v-loading="loading"
:empty-text="'暂无数据'"
:style="style"
>
<el-table-column
v-for="column in tableColumn" :key="column?.label"
v-bind="{...column}"
>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts" setup name="customTable">
const props = defineProps({
tableData: {
type: Array,
default: () => [],
},
tableColumn: {
type: Array<any>,
default: () => [],
},
loading: {
type: Boolean,
default: false,
},
style: {
type: Object,
default: () => {},
},
maxHeight: {
type: [String, Number],
default: 700,
},
});
// 下滑懒加载
const tableContainer = ref(null); // 需要通过它来定位table有滚动条的dom,因为一次可能渲染很多个table,我们不能直接找那个有滚动条的table
const pushCount = ref(1); // 追加数据的次数
const renderData = computed(() => {
return props.tableData.slice(0, pushCount.value * 100); // renderData为渲染的实际数据,pushCount变化会重新计算render的数据
});
const handleScroll = (event) => {
const target = event.target as HTMLElement;
// 判断滚动条是否快要到底了,距离底部还剩700px左右时,就要开始追加了,700为buffer值
if (target.scrollHeight - target.scrollTop - target.clientHeight <= 700) {
// 每一次追加都需要判断,是否还有要追加的数据,如果已经到底了,就没必要追加数据了
if(renderData.value.length < props.tableData.length) {
pushCount.value += 1;
}
}
}
// 把scorllBody放在这个位置,是为了方便onUnmounted时候,取消掉scroll的监听
let scrollBody
// 有时候接收的tableData可能是变化的,一开始不到100,后来超过100,再后来又变成300,通过hasHandleScroll,来限制一个table最多只能加一个滚动 监听
let hasHandleScroll = false
onUnmounted(() => {
hasHandleScroll = false
scrollBody.removeEventListener('scroll', handleScroll, false)
})
// 通过对props.tableData的监听,来判断是否需要开启滚动监听,可能一开始不到100,后来就超过100了,需要加监听
watch(() => props.tableData, () =>{
nextTick(() => {
const container = tableContainer.value as any;
scrollBody = container?.querySelector('.el-scrollbar__wrap')
// 需要判断一下scrollBody 是否已经有了滚动监听
if(props.tableData.length >= pushCount.value * 100 && !hasHandleScroll) {
scrollBody.addEventListener('scroll', handleScroll, false)
hasHandleScroll = true
}
})
},{
immediate: true
})
</script>
<style lang="scss">
</style>
注意
还有更多优化的空间
比如handleScroll可以通过防抖封装一下,但时间间隔不要太大,最好在50ms内。
比如每一次tableData变化时,最好先判断一下,是否需要取消监听scroll。因为有可能最开始超过100,后来又少于100,但少于100以后,如果滚动条仍存在,其实scroll监听仍然存在,这个时候其实不需要监听了。
这种优化方案,仍然不足,当用户有足够耐心,将所有数据加载完了,往上滚动时,页面仍然可能会卡顿。这个时候需要使用虚拟列表的方式来实现优化,而不仅仅只是下拉懒加载。下拉懒加载是虚拟列表的一部分。
如果是pc端,最好用分页,但如果是移动端,分页对用户的体验就不太友好。