• elementui表格性能优化123


    最近公司需要使用vue重构以前的项目,为了节省时间快速开发选择了使用element

    不得不说,咋一看element的功能很全面样式,该有的都用,但是我们的项目对性能要求比较高,特别是表格

    开发过程比较顺利各功能实现都很不难,但是性能测试确成了问题,分页的情况下单页100条就不怎么流畅了,更别说要求不分页5000条,直接加载过程中内存爆掉卡死了,于是开始了分析源码的路;

    找到element/packages/table/src表格的代码都在这里

    入口是table.vue  我们要看的是具体数据生成部分 看名字就知道是table-body.js

    点进去一看,你基本就知道性能慢是个什么情况了,整体使用vue的render方法生成整个界面,整个table部分

    render(h) {
          const columnsHidden = this.columns.map((column, index) => this.isColumnHidden(index));
      return (
      <table
      class="el-table__body"
      cellspacing="0"
      cellpadding="0"
      border="0">
      <colgroup>
      {
      this._l(this.columns, column => <col name={ column.id } />)
      }
      </colgroup>
      <tbody>
      {
      this._l(this.data, (row, $index) =>
      [<tr
      style={ this.rowStyle ? this.getRowStyle(row, $index) : null }
      key={ this.table.rowKey ? this.getKeyOfRow(row, $index) : $index }
      on-dblclick={ ($event) => this.handleDoubleClick($event, row) }
      on-click={ ($event) => this.handleClick($event, row) }
      on-contextmenu={ ($event) => this.handleContextMenu($event, row) }
      on-mouseenter={ _ => this.handleMouseEnter($index) }
      on-mouseleave={ _ => this.handleMouseLeave() }
      class={ [this.getRowClass(row, $index)] }>
      {
      this._l(this.columns, (column, cellIndex) => {
      const { rowspan, colspan } = this.getSpan(row, column, $index, cellIndex);
      if (!rowspan || !colspan) {
      return '';
      } else {
      if (rowspan === 1 && colspan === 1) {
      return (
      <td
      style={ this.getCellStyle($index, cellIndex, row, column) }
      class={ this.getCellClass($index, cellIndex, row, column) }
      on-mouseenter={ ($event) => this.handleCellMouseEnter($event, row) }
      on-mouseleave={ this.handleCellMouseLeave }>
      {
      column.renderCell.call(
      this._renderProxy,
      h,
      {
      row,
      column,
      $index,
      store: this.store,
      _self: this.context || this.table.$vnode.context
      },
      columnsHidden[cellIndex]
      )
      }
      </td>
      );
      } else {
      return (
      <td
      style={ this.getCellStyle($index, cellIndex, row, column) }
      class={ this.getCellClass($index, cellIndex, row, column) }
      rowspan={ rowspan }
      colspan={ colspan }
      on-mouseenter={ ($event) => this.handleCellMouseEnter($event, row) }
      on-mouseleave={ this.handleCellMouseLeave }>
      {
      column.renderCell.call(
      this._renderProxy,
      h,
      {
      row,
      column,
      $index,
      store: this.store,
      _self: this.context || this.table.$vnode.context
      },
      columnsHidden[cellIndex]
      )
      }
      </td>
      );
      }
      }
      })
      }
      </tr>,
      this.store.isRowExpanded(row)
      ? (<tr>
      <td colspan={ this.columns.length } class="el-table__expanded-cell">
      { this.table.renderExpanded ? this.table.renderExpanded(h, { row, $index, store: this.store }) : ''}
      </td>
      </tr>)
      : ''
      ]
      ).concat(
      <el-tooltip effect={ this.table.tooltipEffect } placement="top" ref="tooltip" content={ this.tooltipContent }></el-tooltip>
      )
      }
      </tbody>
      </table>
      );
      },

    我不太清楚render生成界面的时候是否会在数据变更后整体重新渲染界面,就算不会也会有一个变更判断过程,而他是实实在在的重新生成了整个table的vnode对象,还包含一堆子集,然后返回渲染,这个过程感觉不会快,特别是数据量大的时候

    中间的思考过程略过,对比了vue的另一款table控件的源代码vue-easytable并试验了他的性能,决定第一步就是将界面生成方式改为template,并且将点击事件进行了委托

    别的就不说了上代码

    <template>
    <table 
    class="el-table__body"
    cellspacing="0"
    cellpadding="0"
    border="0">
    <colgroup>
                      <col v-for="col in columns" 
                   :key="col.id"
                   :name="col.id"
                   :width="col.width"/>
                    </colgroup>
            <tbody
             @click="handleClick"
             @dblclick="handleDoubleClick"
             @contextmenu="handleContextMenu">
             <tr v-for="(row,$index) in data"
             :style="trStyle(row, $index)"
             :key="trKey(row, $index)"
             @mouseenter="handleMouseEnter($index)"
             @mouseleave="handleMouseLeave"
             :class="trClass(row, $index)"
             :index="$index">
             <td v-for="(column,cellIndex) in columns"
             v-if="getSpan(row, column, $index, cellIndex)"
             :style="getCellStyle($index, cellIndex, row, column)"
             :class="getCellClass($index, cellIndex, row, column)"
             :rowspan="column._rowspan"
             :colspan="column._colspan"
             @mouseenter="handleCellMouseEnter($event, row)"

             @mouseleave="handleCellMouseLeave">

                                    到这里问题就来了,他的生成调用了其他文件里的方法table-column.js,水平有限学vue也没多久,他这个将具体的内容填充生成的方式没看懂,也没太多时间去研究

                                    这个时候有两个思路

                        1.继续沿用他render的生成方式,我是重新写了一个小组件,在这个组件里去使用render生成就行,如下所示

             <ElTableCell
             :store="store"

             :config="config(row,column,$index)"></ElTableCell>

                     2.牺牲他的灵活性,根据你具体的需求在这里手动判断生成想要的界面,你只需要把配置数据传递过来就行 , 这样做对性能提升是巨大的,正如 vue-easytable的编辑模式一样,模式少的可怜,这样也最快

    如果你有多个编辑模式什么的,最好使用v-if,不要用v-show,可以大大提升加载速度,如下所示

                                <div v-else class="cell">
              <div v-else-if="realtype(column)=='input'">
              <div v-if="isHide(row)">{{row[column.keys]}}</div>
              <el-input
              v-else
       type="input"
       size="mini"
       v-show="row.edit"
       v-model="row[column.keys]">
    </el-input>
              </div>
              <div v-else>
              {{row[column.keys]}}
              </div>
             </div>

             </td>
             </tr>
            </tbody>
    </table>
    </template>

    经测试   第一种ElTableCell的方式有一定的性能提升 几百条应该没什么问题,不至于100就卡,但是1000条就会偶尔内存爆掉,单击双击编辑模式什么的速度提升比较明显;

    第二种自定义模板的方式提升就比较大了,5000条没有问题,但是各种操作相对于第一种会慢不少,不过需要传递过来配置数据

    这两种方式在数据量少的时候流畅度提升巨大,特别是几百条的时候感觉比较明显

    原始生产方式的最后一段代码,tr生成的是展开行的具体内容(我不需要就没加),下面的是tooltip提示,暂时来说还不需要

    this.store.isRowExpanded(row)
      ? (<tr>
      <td colspan={ this.columns.length } class="el-table__expanded-cell">
      { this.table.renderExpanded ? this.table.renderExpanded(h, { row, $index, store: this.store }) : ''}
      </td>
      </tr>)
      : ''
      ]
      ).concat(
      <el-tooltip effect={ this.table.tooltipEffect } placement="top" ref="tooltip" content={ this.tooltipContent }></el-tooltip>
      )

    两种方式均需要再下面添加一些属性和方法,这里就不具体说明了,自己看

    。。。有好的方式,可以教教我,感谢

  • 相关阅读:
    MySQL
    docker-compose部署redis及RabbitMq
    docker-compose部署nacos单机版(简洁优化版)
    用U盘启动安装CentOS的详解
    mysql 获取id最大值
    JAVA编码-- 比较两个BigDecimal大小(重要)
    MYSQL如何把年月日3个int类型的字段拼接成日期类型,并按照日期段进行查询
    Mysql如何根据年月日来查询数据
    springboot 调用redisTemplate时总是为null的解决方法
    shell中read用法
  • 原文地址:https://www.cnblogs.com/1549983239yifeng/p/14405596.html
Copyright © 2020-2023  润新知