爲何要吐槽iview的table組件呢?vue
首先iview的table的使用體驗十分的不友好,每次修改數據都會重刷整個表格,性能極差,由於重刷表格會致使自動失焦等問題!node
vue不是有diff算法嗎?重刷表格數據沒有發生變化部分是不會從新渲染的,性能爲何會差呢?算法
是的,diff算法是能夠優化vnode的渲染問題,可是前提是key,tag等屬性沒有發生變化的狀況,但是萬惡的iview table既然私自修改了key的值,致使diff的優化失效。iview
如何解決這個問題?async
table組件data屬性接收的數據源與實際操做的數據分離,例如:如下例子的tempData與data。性能
<Table border :columns="columns" :data="tempData"></Table> export default { data:{ tempData:[], columns:[ { title:'商品價格(元)', width:120, align: 'center', render:(h,params)=>{ let data = this.data[params.index]; // 實際更改的是this.data的數據,由於tempData沒有改變,因此table不會從新渲染 return <i-input maxlength={8} number value={data.value} on-on-blur={(e)=>{data.value=e.target.value} />; } }, ], data:[] }, watch:{ data(val){ this.tempData = util.deepCopy(val); }, } }
table組件的內容主題table-body組件,table-body的data屬性接收的是rebildData優化
<div :class="[prefixCls + '-body']" :style="bodyStyle" ref="body" @scroll="handleBodyScroll" v-show="!((!!localeNoDataText && (!data || data.length === 0)) || (!!localeNoFilteredDataText && (!rebuildData || rebuildData.length === 0)))"> <table-body ref="tbody" :prefix-cls="prefixCls" :styleObject="tableStyle" :columns="cloneColumns" :data="rebuildData" :columns-width="columnsWidth" :obj-data="objData"></table-body> </div>
table組件內部實現深度監聽了data屬性,當data的任意屬性發生變化就會從新生成rebuildData。(深度拷貝一份原有的data)爲何要拷貝一份呢?ui
解:爲了防止table組件內部直接經過params.row直接修改數據源,直接修改數據源容易致使bug難追溯,數據容易錯亂等狀況,增長維護成本!this
watch: { data: { handler () { const oldDataLen = this.rebuildData.length; this.objData = this.makeObjData(); this.rebuildData = this.makeDataWithSortAndFilter(); this.handleResize(); if (!oldDataLen) { this.fixedHeader(); } // here will trigger before clickCurrentRow, so use async setTimeout(() => { this.cloneData = deepCopy(this.data); }, 0); }, deep: true }, ..... }
生成新的data數據,並重置每一項數據的_rowKey屬性(該屬性被table-tr組件看成key來使用),_rowKey是全局遞增的,每一次table從新render,_rowKey都會不斷遞增,致使diff算法失效(致使表格從新渲染的元兇)spa
makeDataWithSortAndFilter () { let data = this.makeDataWithSort(); this.cloneColumns.forEach(col => data = this.filterData(data, col)); return data; }, makeDataWithSort () { let data = this.makeData(); let sortType = 'normal'; let sortIndex = -1; let isCustom = false; for (let i = 0; i < this.cloneColumns.length; i++) { if (this.cloneColumns[i]._sortType !== 'normal') { sortType = this.cloneColumns[i]._sortType; sortIndex = i; isCustom = this.cloneColumns[i].sortable === 'custom'; break; } } if (sortType !== 'normal' && !isCustom) data = this.sortData(data, sortType, sortIndex); return data; },
// 生成新的data makeData () { let data = deepCopy(this.data); data.forEach((row, index) => { row._index = index; row._rowKey = rowKey++; //rowKey 全局的一個變量 初始值 rowKey=0 }); return data; },