Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table: optimize performance #21330

Merged
merged 2 commits into from
Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions packages/table/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ export const cellForced = {
on-input={ this.toggleAllSelection }
value={ this.isAllSelected } />;
},
renderCell: function(h, { row, column, store, $index }) {
renderCell: function(h, { row, column, isSelected, store, $index }) {
return <el-checkbox
nativeOn-click={ (event) => 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
Expand All @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions packages/table/src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Watcher.prototype.mutations = {
}

this.updateTableScrollY();
this.syncFixedTableRowHeight();
},

filterChange(states, options) {
Expand All @@ -111,6 +112,7 @@ Watcher.prototype.mutations = {
}

this.updateTableScrollY();
this.syncFixedTableRowHeight();
},

toggleAllSelection() {
Expand Down Expand Up @@ -144,4 +146,8 @@ Watcher.prototype.updateTableScrollY = function() {
Vue.nextTick(this.table.updateScrollY);
};

Watcher.prototype.syncFixedTableRowHeight = function() {
Vue.nextTick(() => this.table.layout.syncFixedTableRowHeight());
};

export default Watcher;
145 changes: 72 additions & 73 deletions packages/table/src/table-body.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -14,7 +15,8 @@ export default {

components: {
ElCheckbox,
ElTooltip
ElTooltip,
TableRow
},

props: {
Expand All @@ -39,16 +41,25 @@ export default {
border="0">
<colgroup>
{
this.columns.map(column => <col name={ column.id } key={column.id} />)
this.columns
.filter((column, index) => !(this.columnsHidden[index] && this.fixed))
.map(column => <col name={column.id} key={column.id} />)
}
</colgroup>
<tbody>
{
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
}));
}, [])
}
<el-tooltip effect={ this.table.tooltipEffect } placement="top" ref="tooltip" content={ this.tooltipContent }></el-tooltip>
<el-tooltip effect={this.table.tooltipEffect} placement="top" ref="tooltip" content={this.tooltipContent}></el-tooltip>
</tbody>
</table>
);
Expand All @@ -71,6 +82,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');
}
Expand Down Expand Up @@ -238,7 +253,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);
}

Expand Down Expand Up @@ -314,9 +329,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) {
Expand All @@ -328,76 +352,51 @@ export default {
let displayStyle = display ? null : {
display: 'none'
};
return (<tr
style={ [displayStyle, this.getRowStyle(row, $index)] }
class={ rowClasses }
key={ this.getKeyOfRow(row, $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 }>
{
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 (
<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,
this.$createElement,
data,
columnsHidden[cellIndex]
)
}
</td>
);
})
}
</tr>);
const height = this.getRowHeight($index);
const heightStyle = height ? {
height
} : null;

return (
<TableRow
style={[displayStyle, this.getRowStyle(row, $index), heightStyle]}
class={rowClasses}
key={this.getKeyOfRow(row, $index)}
nativeOn-dblclick={($event) => 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}
fixed={this.fixed}
>
</TableRow>
);
},

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;
Expand Down Expand Up @@ -430,7 +429,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
Expand Down Expand Up @@ -464,7 +463,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);
Expand All @@ -478,7 +477,7 @@ export default {
}
return tmp;
} else {
return this.rowRender(row, $index);
return this.rowRender({ row, $index, isSelected, isExpanded });
}
}
}
Expand Down
32 changes: 31 additions & 1 deletion packages/table/src/table-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -113,11 +114,40 @@ class TableLayout {

const noData = !(this.store.states.data && this.store.states.data.length);
this.viewportHeight = this.scrollX ? tableHeight - (noData ? 0 : this.gutterWidth) : tableHeight;

setTimeout(() => {
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;
Expand Down
Loading