vue3 elementUi Table 数据下拉懒加载

发布于:2025-02-21 ⋅ 阅读:(15) ⋅ 点赞:(0)

实现的背景

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端,最好用分页,但如果是移动端,分页对用户的体验就不太友好。


网站公告

今日签到

点亮在社区的每一天
去签到