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>
  )

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

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

原文地址:https://www.cnblogs.com/1549983239yifeng/p/14405596.html