Files
wukuang/patches/vxe-table@4.18.0.patch
T

202 lines
13 KiB
Diff
Raw Normal View History

2026-05-23 14:05:22 +08:00
diff --git a/es/table/src/body.js b/es/table/src/body.js
index c90d6b4d575bd0b238762497323970100f55f4e1..a008df6ff9360069d7bcf9124a930ab93704e575 100644
--- a/es/table/src/body.js
+++ b/es/table/src/body.js
@@ -94,6 +94,48 @@ export default defineVxeComponent({
* 渲染列
*/
const renderTdColumn = (seq, rowid, fixedType, isOptimizeMode, rowLevel, row, rowIndex, $rowIndex, _rowIndex, column, $columnIndex, columns, items) => {
+ // [perf] 视口外单元格 & 固定列隐藏单元格 早期返回
+ const { tableData: _td, tableColumn: _tc, dragRow: _dr, overflowX: _ox, overflowY: _oy, scrollXLoad: _sxl, scrollYLoad: _syl, mergeBodyFlag: _mbf, isAllOverflow: _iao } = tableReactData;
+ const { fullColumnIdData: _fcid, mergeBodyList: _mbl, scrollXStore: _sxs, scrollYStore: _sys } = tableInternalData;
+ const _vyOpts = computeVirtualYOpts.value;
+ const _vxOpts = computeVirtualXOpts.value;
+ const _treeConfig = tableProps.treeConfig;
+ // [perf-1] 固定列区域中,非当前区域的列是隐藏的,直接返回空占位 td
+ // 例如:渲染左固定区域时,中间列和右固定列都是隐藏的
+ // 注意:必须排除有合并单元格的情况,因为合并可能跨越固定/非固定边界
+ const _fixedHidden = _ox && (fixedType ? column.fixed !== fixedType : !!column.fixed);
+ if (_fixedHidden && _iao && !(_mbf && _mbl.length) && !tableProps.spanMethod) {
+ return h('td', {
+ class: 'vxe-table--column vxe-body--column col--ellipsis fixed--hidden',
+ key: column.id,
+ colid: column.id
+ }, [h('div', { key: 'tc', class: 'vxe-cell c--ellipsis' })]);
+ }
+ // [perf-2] 虚拟滚动视口外单元格早期返回
+ if (!(_mbf && _mbl.length) && !tableProps.spanMethod) {
+ if (!_dr || getRowid($xeTable, _dr) !== rowid) {
+ let _canSkip = false;
+ if (_oy && _syl && _td.length > 16 && !_treeConfig && !_vyOpts.immediate) {
+ if (_rowIndex < _sys.visibleStartIndex - _sys.preloadSize || _rowIndex > _sys.visibleEndIndex + _sys.preloadSize) {
+ _canSkip = true;
+ }
+ }
+ if (!_canSkip && _ox && _sxl && _tc.length > 10 && !_vxOpts.immediate && !column.fixed) {
+ const _colRest = _fcid[column.id] || {};
+ const _ci = _colRest._index;
+ if (_ci < _sxs.visibleStartIndex - _sxs.preloadSize || _ci > _sxs.visibleEndIndex + _sxs.preloadSize) {
+ _canSkip = true;
+ }
+ }
+ if (_canSkip) {
+ return h('td', {
+ class: 'vxe-table--column vxe-body--column col--ellipsis is--progress',
+ key: column.id,
+ colid: column.id
+ }, [h('div', { key: 'tc', class: 'vxe-cell c--ellipsis' })]);
+ }
+ }
+ }
const $xeGrid = $xeTable.xeGrid;
const $xeGantt = $xeTable.xeGantt;
const { columnKey, resizable: allResizable, showOverflow: allShowOverflow, border, height, treeConfig, cellClassName: allCellClassName, cellStyle, align: allAlign, spanMethod, mouseConfig, editConfig, editRules, tooltipConfig, padding: allPadding } = tableProps;
@@ -218,14 +260,7 @@ export default defineVxeComponent({
if (isRowDragCell) {
tdOns.onMouseup = $xeTable.triggerCellMouseupEvent;
}
- // 点击事件处理
- tdOns.onClick = (evnt) => {
- $xeTable.triggerCellClickEvent(evnt, cellParams);
- };
- // 双击事件处理
- tdOns.onDblclick = (evnt) => {
- $xeTable.triggerCellDblclickEvent(evnt, cellParams);
- };
+ // [perf] onClick/onDblclick 已迁移到 tbody 事件委托,不再为每个 td 创建闭包
let isMergeCell = false;
let mergeColspan = 1;
let mergeRowspan = 1;
@@ -461,21 +496,7 @@ export default defineVxeComponent({
let _rowIndex = -1;
const hasRowGroupAggregate = isRowGroupStatus && row.isAggregate;
const trOn = {};
- // 当前行事件
- if (rowOpts.isHover || highlightHoverRow) {
- trOn.onMouseover = (evnt) => {
- if (isVMScrollProcess()) {
- return;
- }
- $xeTable.triggerHoverEvent(evnt, { row, rowIndex });
- };
- trOn.onMouseleave = () => {
- if (isVMScrollProcess()) {
- return;
- }
- $xeTable.clearHoverRow();
- };
- }
+ // [perf] 行 hover 事件已迁移到 tbody 事件委托
if (rowRest) {
rowIndex = rowRest.index;
_rowIndex = rowRest._index;
@@ -804,7 +825,81 @@ export default defineVxeComponent({
* 内容
*/
h('tbody', {
- ref: refBodyTBody
+ ref: refBodyTBody,
+ // [perf] 事件委托:用单个 handler 替代每个 td 的 onClick/onDblclick 闭包
+ onClick(evnt) {
+ const td = evnt.target.closest('.vxe-body--column');
+ if (!td) return;
+ const tr = td.closest('.vxe-body--row');
+ if (!tr) return;
+ const colid = td.getAttribute('colid');
+ const rowid = tr.getAttribute('rowid');
+ if (!colid || !rowid) return;
+ const { fullAllDataRowIdData, fullColumnIdData, afterFullData } = tableInternalData;
+ const rowRest = fullAllDataRowIdData[rowid];
+ const colRest = fullColumnIdData[colid];
+ if (!rowRest || !colRest) return;
+ const cellParams = {
+ $table: $xeTable, $grid: $xeTable.xeGrid, $gantt: $xeTable.xeGantt,
+ isEdit: false, seq: rowRest.seq, rowid,
+ row: rowRest.row, rowIndex: rowRest.index,
+ $rowIndex: rowRest.$index, _rowIndex: rowRest._index,
+ column: colRest.column, columnIndex: colRest.index,
+ $columnIndex: colRest._index, _columnIndex: colRest._index,
+ fixed: fixedType, source: sourceType, type: renderType,
+ isHidden: false, level: rowRest.level,
+ visibleData: afterFullData, data: tableReactData.tableData,
+ items: tableReactData.tableData
+ };
+ $xeTable.triggerCellClickEvent(evnt, cellParams);
+ },
+ onDblclick(evnt) {
+ const td = evnt.target.closest('.vxe-body--column');
+ if (!td) return;
+ const tr = td.closest('.vxe-body--row');
+ if (!tr) return;
+ const colid = td.getAttribute('colid');
+ const rowid = tr.getAttribute('rowid');
+ if (!colid || !rowid) return;
+ const { fullAllDataRowIdData, fullColumnIdData, afterFullData } = tableInternalData;
+ const rowRest = fullAllDataRowIdData[rowid];
+ const colRest = fullColumnIdData[colid];
+ if (!rowRest || !colRest) return;
+ const cellParams = {
+ $table: $xeTable, $grid: $xeTable.xeGrid, $gantt: $xeTable.xeGantt,
+ isEdit: false, seq: rowRest.seq, rowid,
+ row: rowRest.row, rowIndex: rowRest.index,
+ $rowIndex: rowRest.$index, _rowIndex: rowRest._index,
+ column: colRest.column, columnIndex: colRest.index,
+ $columnIndex: colRest._index, _columnIndex: colRest._index,
+ fixed: fixedType, source: sourceType, type: renderType,
+ isHidden: false, level: rowRest.level,
+ visibleData: afterFullData, data: tableReactData.tableData,
+ items: tableReactData.tableData
+ };
+ $xeTable.triggerCellDblclickEvent(evnt, cellParams);
+ },
+ // [perf] 行 hover 事件委托
+ onMouseover(evnt) {
+ if (isVMScrollProcess()) return;
+ const { highlightHoverRow } = tableProps;
+ const rowOpts = computeRowOpts.value;
+ if (!(rowOpts.isHover || highlightHoverRow)) return;
+ const tr = evnt.target.closest('.vxe-body--row');
+ if (!tr) return;
+ const rowid = tr.getAttribute('rowid');
+ if (!rowid) return;
+ const rowRest = tableInternalData.fullAllDataRowIdData[rowid];
+ if (!rowRest) return;
+ $xeTable.triggerHoverEvent(evnt, { row: rowRest.row, rowIndex: rowRest.index });
+ },
+ onMouseleave(evnt) {
+ if (isVMScrollProcess()) return;
+ const { highlightHoverRow } = tableProps;
+ const rowOpts = computeRowOpts.value;
+ if (!(rowOpts.isHover || highlightHoverRow)) return;
+ $xeTable.clearHoverRow();
+ }
}, renderRows(fixedType, isOptimizeMode, renderDataList, renderColumnList))
]),
h('div', {
diff --git a/es/table/src/table.js b/es/table/src/table.js
index de124a25e90ece13490b880b5a73e5e4d1ab3a37..493318a575e9c82ad912b3de301073a5d522db93 100644
--- a/es/table/src/table.js
+++ b/es/table/src/table.js
@@ -11761,7 +11761,8 @@ export default defineVxeComponent({
if (isRollX) {
evnt.preventDefault();
internalData.inWheelScroll = true;
- if (browseObj.firefox || browseObj.safari) {
+ // [perf] 虚拟X滚动模式下也走直接赋值,跳过 rAF 延迟
+ if (browseObj.firefox || browseObj.safari || scrollXLoad) {
const currLeftNum = scrollLeft;
setScrollLeft(xHandleEl, currLeftNum);
setScrollLeft(bodyScrollElem, currLeftNum);
@@ -11796,7 +11797,10 @@ export default defineVxeComponent({
if (isRollY) {
evnt.preventDefault();
internalData.inWheelScroll = true;
- if (browseObj.firefox || browseObj.safari) {
+ // [perf] 虚拟滚动模式下,所有浏览器都走直接赋值路径
+ // 原始代码在 Chrome 上使用 scrollTopTo 缓动动画,每个 wheel 事件产生 ~6 帧 rAF 回调
+ // 每帧都设置 5 个元素的 scrollTop + 触发虚拟滚动数据更新,导致严重卡顿
+ if (browseObj.firefox || browseObj.safari || scrollYLoad) {
const currTopNum = scrollTop;
setScrollTop(yHandleEl, currTopNum);
setScrollTop(bodyScrollElem, currTopNum);