elementui表格性能优化123

本文介绍了在使用ElementUI重构项目时遇到的表格性能问题,分析了ElementUI表格的源码,发现数据量大时性能下降。通过对比vue-easytable,决定优化表格的界面生成方式,采用template替代render,并进行事件委托。文中提出了两种优化方案:1. 使用自定义小组件渲染;2. 根据需求手动判断生成界面。测试结果显示,这两种方式在数据量小的情况下性能提升显著,但不同场景下优缺点各异。

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

最近公司需要使用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://github.com/future3002/element.git
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值