当前位置:   article > 正文

el-table 可编辑表格大数据渲染性能优化_el-table 处理大量数据

el-table 处理大量数据

背景与分析

可编辑表格:是指表格单元格是一个form表单元素,或者有可能会变成表单元素。
1、不可分页的表格,大数据渲染
当数据量足够大时,比如说1000条数据,页面渲染就会卡死,需要卡5s到10s,才能将数据渲染到页面,完全渲染出来后,页面的其他操作也是非卡顿,有明显的延迟感。
2、分页表格,用户大量新增数据,或复制、导入大量的数据
因前端分页,初始化时,以及完全渲染之后,页面都很流畅;但是当允许用户新增数据时,限制用户的新增数量也就不会有上述的问题,当不限制时,在完全渲染出来后,页面其他操作也会有明显的延迟感。

现需要对初次渲染卡顿,完全渲染卡顿两个角度,进行性能优化。

技术方案

0、分页表格 —— 限制新增的行数,每次只能新增10行20行的

开发不定需求,需求能肯定最好,但是需求可能会从批量操作,导入等角度考虑,不要进行分页,那我们就只能进行性能优化

1、比较多的解决方案是虚拟加载 —— el-table-virtual-scroll

如果是单行编辑保存,或者是不可编辑的话,也不失为一个好的解决方案,
但是如果是编辑所有行之后,统一保存,可编辑表格,有必填项,运行了form的validate,就不能正常的进行表单校验,因为滚动出可视区域之后,form-item即被销毁,开发只能手写校验方法,且不同的业务功能,会有不同的校验规则,完全共用不了,增加组件的使用难度
同时也不适用,树形表格,有合并行的表格;

2、懒加载 —— 滚动加载

当表格内容大于一定数量后,肯定是可以滚动的,滚动加载,表格滚动条的展示会有问题,需要使用虚拟滚动条,
滚动加载,比如先渲染20条数据,当用户滚动后,再渲染后面20条数据。初始化时,渲染速度很快,但是当用户滚动到一定程度,整个页面会渲染出大量的DOM,导致页面有明显的延迟感

3、点击加载 —— 点击行的时候,去加载行的form表单元素

点击加载,当可编辑时,该行允许用户编辑时,点击后,渲染该行的form表单,当用户点击其他行时,渲染其他行的form表单,销毁当前行的form表单,以控制页面展示dom的数量。但是会有必填字段没填,校验失败的字段,也销毁了,导致后面校验异常,更定位不到校验失败的字段。当然我们可以在校验失败时,也渲染form表单就不会有这个问题。
但是当数据很大时,依旧初次渲染卡顿,完全渲染卡顿,只能是属于提升性能的一个小手段吧

4、任务切片 —— 将初始化大数据渲染,这个大任务进行切片

初始化时渲染所有的数据时,这一整个任务耗时严重,我们把这个任务拆分,渲染20条数据,500ms后渲染后面20条,直至所有数据渲染完毕,可以解决初始化渲染卡顿的问题,但是会有完全渲染后卡顿的问题

5、展示渲染 —— 让浏览器不渲染非可视区的内容

content-visibility: auto,跳过屏幕外的内容渲染,解决完全渲染后卡顿的问题

6、网络传输优化 —— 减少传输内容,内容按需提交,修改哪行提交哪行,只提交新增、编辑、删除的行;

需要后端支持。
主要用来减少接口请求的内容大小,原来1000条数据保存,不管修改多少行,始终都是提交1000行数据,修改哪行提交哪行,减少无效数据的传输,既能减少宽带,接口还能提速,最好,校验也是一样,保存时前端只校验 新增和被修改的行

_recordStatus: ‘add’, ‘update’, ‘delete’, ‘sync’;
add: 新增的行,被修改时,状态依旧是add,被删除后,该数据直接丢掉;
update: 已保存,被修改的行;
delete: 已保存,被删除的行;点击【删除】按钮,直接调用接口删除,或最后【保存】时再调用接口
sync: 已保存,未被修改的行;

分析利弊

各个方案均有利弊,没有完美的解决方案,需要具体的问题具体分析;
多个技术聚合使用,采长补短,达到符合自身的需求;
现说一下我的优化套餐
点击加载 + 任务切片 + 展示渲染,只提交被修改的数据,只校验被修改的数据保存校验时,可以正常校验,并定位到校验失败的字段
缺点:

  1. 勾选状态异常,切片会导致数据更新,之前的选中状态会丢失;
  2. 全选,点击勾选列的标题头上的全选,异常,不能全部勾选,后面异常也不能反选;
  3. 表格tr上的content-visibility不生效

方案落地

1、点击加载
使用el-table的@current-change事件,来触发每一行的激活状态,当处于激活状态时,展示el-form-item,非激活时,只展示span标签;

currentChange(cur, pre) {
    if (!this.showAllEditor) {
      cur &&
        !this.rowDisabled(cur) &&
        (![undefined, null, ''].includes(cur[this.rowKey]) ||
          cur[this.expandKey]) &&
        this.$set(cur, '_editable', true)
      pre &&
        (![undefined, null, ''].includes(pre[this.rowKey]) ||
          pre[this.expandKey]) &&
        this.$set(pre, '_editable', false)
    }
    this.$emit('currentChange', cur, pre)
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2、任务切片

/**
 * * 将大数据分页化处理 —— 切片
 * @param { totalList, currentDefaultList, pageSize, interval } 总数据当前默认数据,切片大小,切片时间间隔
 * @param callback 设置切片数据的回调函数
 * @param complete 完成之后的回调函数
 * @returns 
 */
const pagingList = ({
  totalList,
  currentDefaultList = [],
  pageSize = 10,
  interval = 100,
}, callback: Function, complete?: Function) => {
  if (totalList.length && totalList.length > pageSize) { //* 需要分页时
    let currentList = currentDefaultList; //? 初始化当前数据
    currentList = getNextPage(totalList, currentList, pageSize)
    callback(currentList);
    const timer = setInterval(() => {
      currentList = getNextPage(totalList, currentList, pageSize)
      if (currentList.length < totalList.length) {
        callback(currentList);
      } else {
        clearInterval(timer);
        callback(currentList);
        complete && complete(currentList);
      }
    }, interval);
    return;
  }
  callback(totalList);
  complete && complete(totalList);
}
const getNextPage = (totalList: any[], currentList: any[], pageSize: number = 10) => {
  if (totalList.length && currentList.length < totalList.length) {
    return totalList.slice(0, currentList.length + pageSize);
  }
  return currentList;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
setDataSource(data) {
    pagingList({totalList: data}, (currentList) => {
      this.list = currentList;
    })
    this.listCopy = data
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3、展示渲染

.batchSaveTable {
  ::v-deep {
    .el-table__row .el-table__cell.is-hidden {
      content-visibility: hidden;
    }
    .el-table__row .el-table__cell:not(:has(.el-form-item)) {
      background: pink;
      content-visibility: auto;
    }
    // .el-table__row {
    //   content-visibility: auto;
    // }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4、方案缺陷与修复
4.1、当一个字段校验失败,并且处于非浏览器屏幕时,将不进行渲染,点击【保存】进行前端校验时也就校验不到该字段;
修复:在渲染el-form-item时判断,是否校验失败,校验失败时,展示el-form-item,校验通过时,展示span
4.2、勾选状态异常,切片会导致数据更新,之前的选中状态会丢失;全选,点击勾选列的标题头上的全选,异常,不能全部勾选,后面异常也不能反选;
修复:通过列的属性 :class-name=“tableDataReady ? ‘’ : ‘disabled_selection’”,来控制勾选列的禁用状态,页面执行循环渲染时,勾选列禁用,完全渲染后,用户才可以进行勾选
4.4、表格tr上的content-visibility不生效;
修复: 只能临时在.el-table__row .el-table__cell 上设置 content-visibility: auto;

详细代码


  • 1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/857255
推荐阅读
相关标签
  

闽ICP备14008679号