触发更新
场景树的更新会发生在什么情况下呢?一般来说是在数据改变驱动、用户交互、布局改变 三种情况下触发更新事件流程:
-
数据驱动:当表格的属性发生变化,宽、高、图元变化的时候:分别触发
updateColWidth/updateRowHeight/updateCell -
用户交互:用户与表格产生交互:触发
resize(拖拽列宽)/updateSortIcon(排序图标) -
布局改变:设备尺寸、展示模式变更触发布局上的变化,也会导致表格的更新:
dealWidthMode(自适应布局)
更新流程
1. 属性修改(核心方法:
直接修改图元属性(如位置、尺寸、样式)
- 列宽更新:通过
setAttribute修改列容器的width属性。
updateColWidth(col: number, deltaX: number) { const columnGroup = this.getColGroup(col); columnGroup.setAttribute('width', newWidth); *// 直接修改列宽(触发脏标记)* this.updateContainer(true); *// 触发布局更新* }
- 行高更新:调整行容器的
height属性,同步更新垂直布局。
updateRowHeight(row: number, deltaY: number) { const rowGroup = this.getRowGroup(row); rowGroup.setAttribute('height', newHeight); *// 修改行高* this._updateContainerHeight(row, deltaY); *// 联动容器高度*
2. 布 局重计算(核心方法:
基于属性变化重新计算容器坐标和尺寸,具体来说是通过 _needUpdateContainer 标记控制更新流程,这样可以确保仅当需要更新时依次执行布局计算(列宽/坐标调整)、尺寸同步(表格/冻结区)、组件状态(滚动条)等步骤,接着渲染提交(updateNextFrame),通过 stage.renderNextFrame 触发渲染引擎(渲染下一帧)
updateContainer(async: boolean = false) { if (async) { if (!this._needUpdateContainer) { this._needUpdateContainer = true; setTimeout(() => { this.updateContainerSync(); }, 0); } } else { this._needUpdateContainer = true; this.updateContainerSync(); } } updateContainerSync() { if (!this._needUpdateContainer) { return; } this._needUpdateContainer = false; this.updateContainerAttrWidthAndX(); this.updateTableSize(); this.component.updateScrollBar(); this.updateDomContainer(); this.updateNextFrame(); }
自适应布局:动态分配剩余空间,覆盖手动调整结果。我的解读重点是dealHeightMode和dealwitdhMode:
dealWidthMode() 方法负责处理表格列宽的自适应布局与自动填充逻辑:当开启 widthMode: 'adaptive' 时,首先清空历史列宽缓存,计算行表头及右侧冻结列的固定总宽度,将画布剩余空间按比例分配给非冻结列;若启用 autoFillWidth 则检测内容总宽度是否小于画布宽度,若不足则对非冻结列进行等比放大。完成列宽计算后,遍历主体区域(bodyGroup)、列头区域(colHeaderGroup)、行头区域(rowHeaderGroup)及角头区域(cornerHeaderGroup)的子列元素,累加各列实际宽度并动态设置容器总宽度,最后调整列头与主体的坐标位置,确保列头紧贴角头右侧、主体区域紧贴行头右侧,形成完整的水平布局流,最终通过容器属性更新驱动渲染引擎同步视觉表现。
dealHeightMode处理高度和处理宽度上的逻辑是高度类似、对称的。二者都符合表格自适应布局的核心思想;
3. 脏标记(VRender 内部机制)
标记需重绘的图元,避免全量渲染。脏标记是一种性能优化技术,用于标识需要更新的数据或对象,避免全量计算或渲染,从而提升效率。在图形渲染中,它通过跟踪变化的部分,仅处理“脏”区域,而非整个场景。
4. 异步渲染提交(核心方法:
- 统一提交入口:所有更新最终调用
stage.renderNextFrame()。
updateNextFrame() { this.stage.renderNextFrame(); }