Element-ui 2.8.0版本中提升表格性能,做了哪些事情,原理是什么
背景
项目中一直用element-ui,之前用el-table的时候,发现表格数据较多时,滑动表格就会很卡。我们的表格中只有200行数据,每行大概有30的字段,表格滑动就卡的不行。在Element-ui 2.8.0版本中,对表格性能进行了优化,链接。短短只有几行代码,却解决了表格的性能问题,今天我们来深度剖析,具体是怎么做到的。
先看改变的代码
代码很简单,增加了一个watch,watch中给当前hover的dom增加"hover-row' class,之前的dom移除'hover-row'。并在214行把添加hover-row的代码去掉了。
听听了上面的我说的是不是更蒙了,很正常。那我们在再来看看具体的代码
上图的三段代码都在element-ui源码的 packages/table/src/table-body.js中。
如上图所示,table在渲染时,会调用getRowClass方法,在getRowClass方法中,会判断当前的行是不是hover的那一行,是的话就增加hover-row这个类。给每一行都增加mouseenter和mouseleave, mouseenter时把vuex中的hoverRow置为当前行,mouseleave时把hoverRow置为空。
问题出在哪?
代码表面看上去没有任何问题,但是仔细一想,只要我们把鼠标放在表格上移动,就会触发mouseenter和mouseleave,vuex中的hoveRow就会发生变化,表格就会重现渲染,render就会重新运行一次。render重新执行一次,getRowStyle有多少行就会执行多少次,getCellStyle和getCellClass(绑在单元格上的方法)有多少个单元格就会执行多少次。假设我们的表格是30*200,那么getRowStyle会执行200次,getCellStyle和getCellClass各执行6000次。还有很多绑在table上的方法都会执行。执行这么多方法,能不卡吗?
官方怎么解决
首先去掉了添加hover-row的语句。
问题:性能问题出在这里?删掉这段代码鼠标移动的时候就不会重新render?
答:问题就出在这里,删掉这段代码鼠标移动不会重新render。只要删掉这段代码,table reder的时候,this.store.states.hoverRow并不会被touch,鼠标移动的时候this.store.states.hoverRow虽然发生变化,但是并不会触发页面的重新渲染,这就是依赖收集的意义。
依赖收集:当 Vue Component render 函数被执行的时候, data 上会被 触碰(touch), 即被读, getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data
这时这段代码就更容易理解了,检测vuex中hoverRow的变化,通过直接修改dom的方法来添加类名。
问题:不是说操作dom性能不好,为什么这里要直接操作dom
答:首先直接操作dom性能是要优于vdom,因为需要把vdom转化为真实的dom,但是vdom能够批量更新,且能跨平台,数据和view分离效率也更高。如果我们只是更新单个数据,且页面重新reder很复杂时,直接操作dom也是一种解决办法。当然得看具体情况使用。
我会怎么解决这个问题
如果是我来做,我不会通过js才给一个dom hover增加样式,因为只用css就能搞定了,明明一段css就能搞定的事,为什么要用js来做
.el-table__body tr:hover { background-color: ...; }
总结
(1)在嵌套循环中,不要用函数,因为每次reder函数都会执行很多遍
(2)善用缓存,善用computed