vue + el-table 虚拟列表的实现

本文介绍了一种使用虚拟列表技术来优化大量数据展示的方法,通过仅渲染可视区域内的数据减少页面加载时间和提高用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

不到万不得已不建议使用虚拟列表,建议使用分页和过滤器。

但有些时候事与愿违,功能就必须要把列表全展示下来,列表多达上万条数据,并且字段高达100,这样下来接口访问10s以上,就更别说页面渲染的压力了,没办法只能用虚拟列表吧,废话不多说,直接开干。

1.原理,借用网图

 思路也比较明了,一共有n条数据,但我们只渲染可视区域的局部数据,通过滚动列表从而得知当前滚动的距离,计算出开始索引,从而获取新的展示区域的局部数据。

2.准备条件

开始索引:start,用于总数据切片头部

结束索引:end ,用于总数据切片尾部

每行表格行高:ItemHeight,(这边需求是等高的列表)

可视区域的Item数量:num ,(最多展示多少行)

3.开始实现

<div ref="listWrap" style="height:100px;overflow-y: scroll;" @scroll="scrollListener">
  <div ref="list">
    <el-table ref="scrollTable" v-loading="tableLoading" :data="showList">
            ...
    </el-table>
  </div>
</div>
....
data(){
    return {
      tableData:[], // 总共的列表数据
      itemHeight: 40, // item高度
      num: 10, // 展示的数据
      start: 0, // 开始索引
      end: 9 // 结束索引
    }
}
mounted() {
    this.$refs.listWrap.style.height = '100px' // 设置可视区域的高度
},
watch:{
    length(val){
        // 超过10行数据,就按照最大40*10 400px高度的列表就行
        if (val >= 10) {
          this.$refs.listWrap.style.height = this.itemHeight * this.num+ 'px' 
        } else {
        // 不足10行数据,这边 加57是因为表头的高度,具体情况,你们加不加无所谓
          this.$refs.listWrap.style.height = this.itemHeight * val + 57 + 'px'
        }
    }
},
computed: {
    // 获取展示的列表
    showList() {
      return this.tableData.slice(this.start, this.end)
    },
    // 获取列表长度
    length() {
      return this.tableData.length || 0
    }
  },
methods: {
    scrollListener() {
      // 获取滚动高度
      const scrollTop = this.$refs.listWrap.scrollTop
      // 开始的数组索引
      this.start = Math.floor(scrollTop / this.itemHeight)
      // 结束索引
      this.end = this.start + this.num
      this.$refs.list.style.transform = `translateY(${this.start * this.itemHeight}px)`// 对列表项y轴偏移
    },
    // ajax获取 tableData
    getData(){
        xxx().then(()=>{
            .... 
            this.tableData = xxx
        }).catch()
    }
  }

设置初始盒子高度100px,然后获得总数据之后,通过计算属性计算出展示的数据数量不足10个按照个数计算展示高度即可,超过10个最多也是最大高度,我这边是400

这样简单的一个虚拟列表完善了,当然几千条数据 就算每行100个字段也不会这么卡,因为我这边还有好多输入框嵌套在表格中,不免会卡顿

4. 继续优化

当数据量过多的时候,用户需要拉到右侧去操作其他数据,这时候需要滑动到底部再看很影响用户体验,可以监听一个滚动条和el-table的横向滚动条滚动距离一致,然后将el-table自带的overflow-x:hidden !important,注意要加层级,这样可以增加用户体验

<div ref="listWrap" style="height:100px;overflow-y: scroll;" @scroll="scrollListener">
  <div ref="list">
    <el-table ref="scrollTable" v-loading="tableLoading" :data="showList">
            ...
    </el-table>
  </div>
</div>
<div ref="bottomScroll" class="bottom-scroll">
   <div class="bottom-scroll-content" :style="{ width: bottomScrollWidth }" />
</div>

...
watch:{
  bottomScrollWidth: {
    // 两个滚动条同时滚动
    handler(newVal, oldVal) {
      // 监听滚动条
      this.$nextTick(() => {
        this.tableDom = this.$refs.scrollTable.bodyWrapper
        this.tableDom.addEventListener('scroll', () => {
          // 与表格滚动条同步
          const scrollLeft = this.tableDom.scrollLeft
          this.$refs.bottomScroll.scrollTo(scrollLeft, 0)
        })
        const bottomScroll= this.$refs.bottomScroll
        bottomScroll.addEventListener('scroll', () => {
          // 与表格滚动条同步
          const scrollLeft = this.$refs.bottomScroll.scrollLeft
          this.$refs.scrollTable.bodyWrapper.scrollTo(scrollLeft, 0)
        })
      })
    },
    deep: true
  }
.... scoped
.bottom-scroll {
    width: 100%;
    overflow-x: auto;
  }
  .bottom-scroll-content {
    /* 高度不设置的话滚动条出不来 */
    height: 1px;
  }
// 让该页面的横向滚动条隐藏
/deep/.el-table--scrollable-x .el-table__body-wrapper{
    overflow-x: hidden !important;
  }
...

总代码如下

<div ref="listWrap" style="height:100px;overflow-y: scroll;" @scroll="scrollListener">
  <div ref="list">
    <el-table ref="scrollTable" v-loading="tableLoading" :data="showList">
            ...
    </el-table>
  </div>
</div>
<div ref="bottomScroll" class="bottom-scroll">
   <div class="bottom-scroll-content" :style="{ width: bottomScrollWidth }" />
</div>
....
data(){
    return {
      tableData:[], // 总共的列表数据
      itemHeight: 40, // item高度
      num: 10, // 展示的数据
      start: 0, // 开始索引
      end: 9 // 结束索引
    }
}
mounted() {
    this.$refs.listWrap.style.height = '100px' // 设置可视区域的高度
},
watch:{
    length(val){
        // 超过10行数据,就按照最大40*10 400px高度的列表就行
        if (val >= 10) {
          this.$refs.listWrap.style.height = this.itemHeight * this.num+ 'px' 
        } else {
        // 不足10行数据,这边 加57是因为表头的高度,具体情况,你们加不加无所谓
          this.$refs.listWrap.style.height = this.itemHeight * val + 57 + 'px'
        }
    },
bottomScrollWidth: {
    // 两个滚动条同时滚动
    handler(newVal, oldVal) {
      // 监听滚动条
      this.$nextTick(() => {
        this.tableDom = this.$refs.scrollTable.bodyWrapper
        this.tableDom.addEventListener('scroll', () => {
          // 与表格滚动条同步
          const scrollLeft = this.tableDom.scrollLeft
          this.$refs.bottomScroll.scrollTo(scrollLeft, 0)
        })
        const bottomScroll= this.$refs.bottomScroll
        bottomScroll.addEventListener('scroll', () => {
          // 与表格滚动条同步
          const scrollLeft = this.$refs.bottomScroll.scrollLeft
          this.$refs.scrollTable.bodyWrapper.scrollTo(scrollLeft, 0)
        })
      })
    },
    deep: true
  }
},
computed: {
    // 获取展示的列表
    showList() {
      return this.tableData.slice(this.start, this.end)
    },
    // 获取列表长度
    length() {
      return this.tableData.length || 0
    }
  },
methods: {
    scrollListener() {
      // 获取滚动高度
      const scrollTop = this.$refs.listWrap.scrollTop
      // 开始的数组索引
      this.start = Math.floor(scrollTop / this.itemHeight)
      // 结束索引
      this.end = this.start + this.num
      this.$refs.list.style.transform = `translateY(${this.start * this.itemHeight}px)`// 对列表项y轴偏移
    },
    // ajax获取 tableData
    getData(){
        xxx().then(()=>{
            .... 
            this.tableData = xxx
        }).catch()
    }
  }
.... scoped
.bottom-scroll {
    width: 100%;
    overflow-x: auto;
  }
  .bottom-scroll-content {
    /* 高度不设置的话滚动条出不来 */
    height: 1px;
  }
// 让该页面的横向滚动条隐藏
/deep/.el-table--scrollable-x .el-table__body-wrapper{
    overflow-x: hidden !important;
  }
...

效果如下 

 

这样可以实现多数据量也不会卡顿的情况,至于后端需要查询10多秒,就让后端优化sql什么的,但注意的是,一旦用虚拟列表,索引将不复存在,无法通过索引作为使用条件,因为可视区域下都是临时虚拟索引,一旦滚动条滚动,索引将毫无意义。

### 实现 Vue3 中 Element Plus `el-table` 组件的虚拟列表功能 为了在 Vue3 和 Element Plus 的环境中实现带有虚拟滚动特性的表格,可以通过二次封装现有的 `el-table` 组件来达到目的。通过引入第三方库如 `vue-virtual-scroller` 或者利用 Element Plus 提供的相关属性和事件监听器,能够有效地提升大型数据集下的性能表现。 #### 方法一:基于现有组件扩展 如果希望保持原生体验并最小化外部依赖,则可以在不改变原有 API 设计的前提下增强其能力。具体做法是在原有的基础上增加对大数据量的支持特性——即所谓的“懒加载”。这通常涉及到只渲染可见区域内的行项,并随着用户的滚动动态更新这些行的内容[^4]。 ```html <template> <div style="height: 100%;"> <!-- 使用 el-scrollbar 来包裹 table --> <el-scrollbar wrap-class="scrollbar-wrapper"> <el-table ref="tableRef" :data="visibleData" @scroll="handleScroll"> <!-- 列定义省略... --> </el-table> </el-scrollbar> </div> </template> <script lang="ts"> import { defineComponent, computed, onMounted } from 'vue'; export default defineComponent({ props: { dataSource: Array, }, setup(props) { const visibleData = computed(() => { // 计算当前可视范围内的数据... return []; }); function handleScroll(event: Event) { // 处理滚动逻辑... } onMounted(() => { // 初始化时可能需要做一些准备工作... }); return { visibleData, handleScroll, }; } }); </script> <style scoped> .scrollbar-wrapper { height: calc(100vh - 200px); /* 根据实际需求调整 */ } </style> ``` 此方案的核心在于如何高效地管理哪些记录应该被呈现给用户查看以及何时触发新的请求获取更多数据。需要注意的是,这种方法虽然简单易懂,但在某些复杂场景下可能会遇到边界情况难以处理的问题。 #### 方法二:集成专门用于优化长列表展示效率的插件 另一种更为推荐的方式是借助成熟的解决方案比如 `vue-virtual-scroller` 进行开发。这类工具已经解决了许多潜在的技术难题,并提供了良好的用户体验保障。下面是一个简单的例子说明如何将其应用于 `el-table`: ```bash npm install vue-virtual-scroller --save ``` 接着修改模板结构如下所示: ```html <template> <RecycleScroller class="scroller" :items="dataSource" :item-size="50" key-field="id" v-slot="{ item }" > <el-row type="flex" justify="space-between" align="middle"> <el-col>{{ item.name }}</el-col> <el-col>{{ item.age }}</el-col> <!-- 更多字段按需添加 --> </el-row> </RecycleScroller> </template> <script lang="ts"> // 导入必要的模块... export default defineComponent({ components: { RecycleScroller, }, props: ['dataSource'], methods: {} }) </script> <style scoped> .scroller { height: 600px; } </style> ``` 上述代码片段展示了如何将 `RecycleScroller` 与 `el-table` 结合起来创建一个高效的虚拟滚动表格。这种方式不仅简化了开发者的工作流程,还确保了即使面对海量数据也能提供流畅的操作感受。 ### 注意事项 当采用任何一种技术路径实施虚拟滚动机制时,请务必考虑以下几个方面的影响因素: - 数据源的变化频率; - 用户交互模式及其预期响应速度; - 浏览器兼容性和跨平台一致性等问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值