From d0e12cde1c62e7e1453a90fc2881103a3e08963d Mon Sep 17 00:00:00 2001 From: cs1707 Date: Sat, 18 Sep 2021 17:29:53 +0800 Subject: [PATCH] Table: optimize performance --- packages/table/src/config.js | 11 +- packages/table/src/table-body.js | 158 +++++++++++++++-------------- packages/table/src/table-layout.js | 30 +++++- packages/table/src/table-row.js | 96 ++++++++++++++++++ packages/table/src/table.vue | 4 +- 5 files changed, 213 insertions(+), 86 deletions(-) create mode 100644 packages/table/src/table-row.js diff --git a/packages/table/src/config.js b/packages/table/src/config.js index ba705ac632d..8d7fd8a06f1 100644 --- a/packages/table/src/config.js +++ b/packages/table/src/config.js @@ -35,12 +35,13 @@ export const cellForced = { nativeOn-click={ this.toggleAllSelection } value={ this.isAllSelected } />; }, - renderCell: function(h, { row, column, store, $index }) { + renderCell: function(h, { row, column, isSelected, store, $index }) { return event.stopPropagation() } - value={ store.isSelected(row) } + value={ isSelected } disabled={ column.selectable ? !column.selectable.call(null, row, $index) : false } - on-input={ () => { store.commit('rowSelectedChanged', row); } } />; + on-input={ () => { store.commit('rowSelectedChanged', row); } } + />; }, sortable: false, resizable: false @@ -67,9 +68,9 @@ export const cellForced = { renderHeader: function(h, { column }) { return column.label || ''; }, - renderCell: function(h, { row, store }) { + renderCell: function(h, { row, store, isExpanded }) { const classes = ['el-table__expand-icon']; - if (store.states.expandRows.indexOf(row) > -1) { + if (isExpanded) { classes.push('el-table__expand-icon--expanded'); } const callback = function(e) { diff --git a/packages/table/src/table-body.js b/packages/table/src/table-body.js index 32e1053d36d..226f4774dba 100644 --- a/packages/table/src/table-body.js +++ b/packages/table/src/table-body.js @@ -6,6 +6,7 @@ import ElTooltip from 'element-ui/packages/tooltip'; import debounce from 'throttle-debounce/debounce'; import LayoutObserver from './layout-observer'; import { mapStates } from './store/helper'; +import TableRow from './table-row.js'; export default { name: 'ElTableBody', @@ -14,7 +15,8 @@ export default { components: { ElCheckbox, - ElTooltip + ElTooltip, + TableRow }, props: { @@ -39,16 +41,23 @@ export default { border="0"> { - this.columns.map(column => ) + this.columns.map(column => ) } { data.reduce((acc, row) => { - return acc.concat(this.wrappedRowRender(row, acc.length)); + const isSelected = this.store.isSelected(row); + const isExpanded = this.store.states.expandRows.indexOf(row) > -1; + return acc.concat(this.wrappedRowRender({ + row, + $index: acc.length, + isSelected, + isExpanded + })); }, []) } - + ); @@ -61,7 +70,15 @@ export default { ...mapStates({ data: 'data', - columns: 'columns', + columns(states) { + if (this.fixed === true || this.fixed === 'left') { + return states.fixedColumns; + } + if (this.fixed === 'right') { + return states.rightFixedColumns; + } + return states.columns; + }, treeIndent: 'indent', leftFixedLeafCount: 'fixedLeafColumnsLength', rightFixedLeafCount: 'rightFixedLeafColumnsLength', @@ -71,6 +88,10 @@ export default { hasExpandColumn: states => states.columns.some(({ type }) => type === 'expand') }), + columnsHidden() { + return this.columns.map((column, index) => this.isColumnHidden(index)); + }, + firstDefaultColumnIndex() { return arrayFindIndex(this.columns, ({ type }) => type === 'default'); } @@ -119,10 +140,8 @@ export default { }, isColumnHidden(index) { - if (this.fixed === true || this.fixed === 'left') { - return index >= this.leftFixedLeafCount; - } else if (this.fixed === 'right') { - return index < this.columnsCount - this.rightFixedLeafCount; + if (this.fixed === true || this.fixed === 'left' || this.fixed === 'right') { + return false; } else { return (index < this.leftFixedLeafCount) || (index >= this.columnsCount - this.rightFixedLeafCount); } @@ -238,7 +257,7 @@ export default { if (cell) { const column = getColumnByCell(table, cell); - const hoverState = table.hoverState = {cell, column, row}; + const hoverState = table.hoverState = { cell, column, row }; table.$emit('cell-mouse-enter', hoverState.row, hoverState.column, hoverState.cell, event); } @@ -314,9 +333,18 @@ export default { table.$emit(`row-${name}`, row, column, event); }, - rowRender(row, $index, treeRowData) { + getRowHeight(rowKey) { + const { fixed } = this; + if (!fixed) { + return null; + } + const height = (this.tableLayout.fixedColumnsBodyRowsHeight || {})[rowKey]; + return typeof height === 'number' ? `${height}px` : height; + }, + + rowRender({ row, $index, treeRowData, isSelected, isExpanded }) { const { treeIndent, columns, firstDefaultColumnIndex } = this; - const columnsHidden = columns.map((column, index) => this.isColumnHidden(index)); + const rowClasses = this.getRowClass(row, $index); let display = true; if (treeRowData) { @@ -328,76 +356,50 @@ export default { let displayStyle = display ? null : { display: 'none' }; - return ( 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 }> - { - columns.map((column, cellIndex) => { - const { rowspan, colspan } = this.getSpan(row, column, $index, cellIndex); - if (!rowspan || !colspan) { - return null; - } - const columnData = { ...column }; - columnData.realWidth = this.getColspanRealWidth(columns, colspan, cellIndex); - const data = { - store: this.store, - _self: this.context || this.table.$vnode.context, - column: columnData, - row, - $index - }; - if (cellIndex === firstDefaultColumnIndex && treeRowData) { - data.treeNode = { - indent: treeRowData.level * treeIndent, - level: treeRowData.level - }; - if (typeof treeRowData.expanded === 'boolean') { - data.treeNode.expanded = treeRowData.expanded; - // 表明是懒加载 - if ('loading' in treeRowData) { - data.treeNode.loading = treeRowData.loading; - } - if ('noLazyChildren' in treeRowData) { - data.treeNode.noLazyChildren = treeRowData.noLazyChildren; - } - } - } - return ( - this.handleCellMouseEnter($event, row) } - on-mouseleave={ this.handleCellMouseLeave }> - { - column.renderCell.call( - this._renderProxy, - this.$createElement, - data, - columnsHidden[cellIndex] - ) - } - - ); - }) - } - ); + const height = this.getRowHeight($index); + const heightStyle = height ? { + height + } : null; + + return ( + this.handleDoubleClick($event, row)} + nativeOn-click={($event) => this.handleClick($event, row)} + nativeOn-contextmenu={($event) => this.handleContextMenu($event, row)} + nativeOn-mouseenter={_ => this.handleMouseEnter($index)} + nativeOn-mouseleave={this.handleMouseLeave} + columns={columns} + row={row} + index={$index} + store={this.store} + context={this.context || this.table.$vnode.context} + firstDefaultColumnIndex={firstDefaultColumnIndex} + treeRowData={treeRowData} + treeIndent={treeIndent} + columnsHidden={this.columnsHidden} + getSpan={this.getSpan} + getColspanRealWidth={this.getColspanRealWidth} + getCellStyle={this.getCellStyle} + getCellClass={this.getCellClass} + handleCellMouseEnter={this.handleCellMouseEnter} + handleCellMouseLeave={this.handleCellMouseLeave} + isSelected={isSelected} + isExpanded={isExpanded} + > + + ); }, - wrappedRowRender(row, $index) { + wrappedRowRender({ row, $index, isSelected, isExpanded }) { const store = this.store; const { isRowExpanded, assertRowKey } = store; const { treeData, lazyTreeNodeMap, childrenColumnName, rowKey } = store.states; if (this.hasExpandColumn && isRowExpanded(row)) { const renderExpanded = this.table.renderExpanded; - const tr = this.rowRender(row, $index); + const tr = this.rowRender({ row, $index, isSelected, isExpanded }); if (!renderExpanded) { console.error('[Element Error]renderExpanded is required.'); return tr; @@ -430,7 +432,7 @@ export default { treeRowData.loading = cur.loading; } } - const tmp = [this.rowRender(row, $index, treeRowData)]; + const tmp = [this.rowRender({ row, $index, treeRowData, isSelected, isExpanded })]; // 渲染嵌套数据 if (cur) { // currentRow 记录的是 index,所以还需主动增加 TreeTable 的 index @@ -464,7 +466,7 @@ export default { } } i++; - tmp.push(this.rowRender(node, $index + i, innerTreeRowData)); + tmp.push(this.rowRender({ row: node, $index: $index + i, treeRowData: innerTreeRowData, isSelected, isExpanded })); if (cur) { const nodes = lazyTreeNodeMap[childKey] || node[childrenColumnName]; traverse(nodes, cur); @@ -478,7 +480,7 @@ export default { } return tmp; } else { - return this.rowRender(row, $index); + return this.rowRender({ row, $index, isSelected, isExpanded }); } } } diff --git a/packages/table/src/table-layout.js b/packages/table/src/table-layout.js index 6365dc9b550..0691b07eca0 100644 --- a/packages/table/src/table-layout.js +++ b/packages/table/src/table-layout.js @@ -25,6 +25,7 @@ class TableLayout { this.bodyHeight = null; // Table Height - Table Header Height this.fixedBodyHeight = null; // Table Height - Table Header Height - Scroll Bar Height this.gutterWidth = scrollbarWidth(); + this.fixedColumnsBodyRowsHeight = {}; for (let name in options) { if (options.hasOwnProperty(name)) { @@ -113,11 +114,38 @@ class TableLayout { const noData = !(this.store.states.data && this.store.states.data.length); this.viewportHeight = this.scrollX ? tableHeight - (noData ? 0 : this.gutterWidth) : tableHeight; - + this.syncFixedTableRowHeight(); this.updateScrollY(); this.notifyObservers('scrollable'); } + syncFixedTableRowHeight() { + const fixedColumns = this.store.states.fixedColumns; + const rightFixedColumns = this.store.states.rightFixedColumns; + if (fixedColumns.length + rightFixedColumns.length === 0) { + return; + } + const { bodyWrapper } = this.table.$refs; + const tableRect = bodyWrapper.getBoundingClientRect(); + + if (tableRect.height !== undefined && tableRect.height <= 0) { + return; + } + const bodyRows = bodyWrapper.querySelectorAll('.el-table__row') || []; + + const fixedColumnsBodyRowsHeight = [].reduce.call( + bodyRows, + (acc, row, index) => { + const height = + row.getBoundingClientRect().height || 'auto'; + acc[index] = height; + return acc; + }, + {} + ); + this.fixedColumnsBodyRowsHeight = fixedColumnsBodyRowsHeight; + }; + headerDisplayNone(elm) { if (!elm) return true; let headerChild = elm; diff --git a/packages/table/src/table-row.js b/packages/table/src/table-row.js new file mode 100644 index 00000000000..fefbfca8f05 --- /dev/null +++ b/packages/table/src/table-row.js @@ -0,0 +1,96 @@ +export default { + name: 'ElTableRow', + props: [ + 'columns', + 'row', + 'index', + 'isSelected', + 'isExpanded', + 'store', + 'context', + 'firstDefaultColumnIndex', + 'treeRowData', + 'treeIndent', + 'columnsHidden', + 'getSpan', + 'getColspanRealWidth', + 'getCellStyle', + 'getCellClass', + 'handleCellMouseLeave', + 'handleCellMouseEnter' + ], + render() { + const { + columns, + row, + index: $index, + store, + context, + firstDefaultColumnIndex, + treeRowData, + treeIndent, + columnsHidden = [], + isSelected, + isExpanded + } = this; + + return ( + + { + columns.map((column, cellIndex) => { + const { rowspan, colspan } = this.getSpan(row, column, $index, cellIndex); + if (!rowspan || !colspan) { + return null; + } + const columnData = { ...column }; + columnData.realWidth = this.getColspanRealWidth(columns, colspan, cellIndex); + const data = { + store, + isSelected, + isExpanded, + _self: context, + column: columnData, + row, + $index + }; + if (cellIndex === firstDefaultColumnIndex && treeRowData) { + data.treeNode = { + indent: treeRowData.level * treeIndent, + level: treeRowData.level + }; + if (typeof treeRowData.expanded === 'boolean') { + data.treeNode.expanded = treeRowData.expanded; + // 表明是懒加载 + if ('loading' in treeRowData) { + data.treeNode.loading = treeRowData.loading; + } + if ('noLazyChildren' in treeRowData) { + data.treeNode.noLazyChildren = treeRowData.noLazyChildren; + } + } + } + return ( + this.handleCellMouseEnter($event, row)} + on-mouseleave={this.handleCellMouseLeave} + > + { + column.renderCell.call( + this._renderProxy, + this.$createElement, + data, + columnsHidden[cellIndex] + ) + } + + ); + }) + } + + ); + } +}; diff --git a/packages/table/src/table.vue b/packages/table/src/table.vue index e0061e6282e..7de924a23fa 100644 --- a/packages/table/src/table.vue +++ b/packages/table/src/table.vue @@ -115,7 +115,7 @@ :row-class-name="rowClassName" :row-style="rowStyle" :style="{ - width: bodyWidth + width: layout.fixedWidth ? layout.fixedWidth + 'px' : '' }">