【高心星出品】
文章目录
- ArkUI组件动态操作:自定义FrameNode
-
-
- 概述:动态操作的价值与原理
-
- 1.1 核心问题与解决方案
- 1.2 组件预创建原理
-
- FrameNode自定义节点的核心优势
-
- 2.1 减少自定义组件创建开销
- 2.2 组件更新更快
- 2.3 直接操作组件树
-
- 动态操作组件的实现方法
-
- 3.1 动态添加组件(四步流程)
-
- 步骤1:创建自定义节点
- 步骤2:实现NodeController
- 步骤3:实现makeNode()方法
- 步骤4:显示自定义节点
- 3.2 动态删除组件
- 3.3 动态更新组件
-
- NodeController生命周期管理
-
- 总结与最佳实践
-
ArkUI组件动态操作:自定义FrameNode
1. 概述:动态操作的价值与原理
1.1 核心问题与解决方案
在传统的声明式开发范式中,组件只能在build()生命周期中创建,这常常导致页面加载缓慢、用户体验不佳。ArkUI框架为此引入了组件动态操作 机制,允许开发者在非build()阶段进行组件的预创建、动态添加、更新和卸载。
核心价值:
- 性能提升:通过预创建组件,减少页面渲染时的创建时间
- 模块化开发:促进独立逻辑的封装与复用
- 运行时灵活性:支持根据实际需求动态加载和卸载组件
1.2 组件预创建原理
组件预创建打破了声明式范式只能在build()中创建组件的限制。开发者可以在页面加载前的空闲时间(如动画执行过程)预创建组件并进行属性设置、布局计算等操作。当页面需要显示时,只需更新属性和布局,从而显著缩短页面响应时间。

图:组件预创建原理示意图,展示如何利用空闲时间预创建组件以加速渲染
2. FrameNode自定义节点的核心优势
2.1 减少自定义组件创建开销
使用声明式自定义组件时,每个节点都需要分配内存空间存储组件实例和状态变量,并执行依赖关系收集等操作,效率较低。FrameNode避免了这些开销:
- 无需创建自定义组件对象和状态变量对象
- 无需进行依赖收集
- 显著提升组件创建速度
2.2 组件更新更快
在动态布局更新场景中,声明式范式依赖数据驱动的自动更新,伴随大量diff计算。对于复杂组件树(深度>30层,包含100-200个组件),在120Hz刷新率下难以保持满帧运行。
FrameNode扩展使框架能够自主掌控更新流程,实现高效的按需剪枝。对于特定业务的动态布局框架,可实现快速更新操作。

2.3 直接操作组件树
声明式范式难以直接调整组件树结构关系(如将子树从一个节点移到另一个节点),通常需要重新渲染整棵树。FrameNode允许通过操作节点直接操控子树,实现局部渲染刷新,性能更优。

图:声明式整树重渲染与FrameNode局部子树操作对比示意图
3. 动态操作组件的实现方法
3.1 动态添加组件(四步流程)
动态添加组件需要遵循以下标准流程:
步骤1:创建自定义节点
准备需要挂载的节点,使用@Builder装饰器定义组件构建函数。
typescript
@Builder
function testBuilder(params: Params) {
Column() {
Text(params.text)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
}
步骤2:实现NodeController
继承NodeController抽象类,管理节点的创建、显示和更新。
typescript
class TextNodeController extends NodeController {
private textNode: BuilderNode<[Params]> | null = null;
private message: string = '';
constructor(message: string) {
super();
this.message = message;
}
makeNode(context: UIContext): FrameNode | null {
return null; // 待实现
}
}
步骤3:实现makeNode()方法
创建BuilderNode实例并构建组件树,返回要显示的节点。
typescript
makeNode(context: UIContext): FrameNode | null {
// 创建BuilderNode实例
this.textNode = new BuilderNode(context);
// 构建组件树
this.textNode.build(
wrapBuilder<[Params]>(testBuilder),
new Params(this.message)
);
// 返回要显示的节点
return this.textNode.getFrameNode();
}
步骤4:显示自定义节点
使用NodeContainer容器和对应的NodeController显示节点。
typescript
@Entry
@Component
struct Index {
private textNodeController: TextNodeController = new TextNodeController("hello");
build() {
Column() {
NodeContainer(this.textNodeController)
.width('100%')
.height(100)
}
}
}
3.2 动态删除组件
通过条件控制语句将NodeContainer节点从界面上移除。
typescript
if (this.isShow) {
NodeContainer(this.textNodeController)
.width('100%')
.height(100)
}
Button('isShow').onClick(() => {
this.isShow = false; // 点击后移除节点
})
3.3 动态更新组件
通过NodeController的rebuild()方法触发makeNode()回调,刷新NodeContainer上显示的节点。
typescript
class TextNodeController extends NodeController {
// ... 其他代码
replaceBuilderNode(newNode: BuilderNode<Object[]>) {
this.textNode = newNode;
this.rebuild(); // 重新调用makeNode方法
}
}
// 使用示例
Button('replaceNode').onClick(() => {
this.textNodeController.replaceBuilderNode(this.buildNewNode());
})
4. NodeController生命周期管理
NodeController用于控制和反馈对应NodeContainer上节点的行为,提供以下关键生命周期方法:
| 方法 | 调用时机 | 用途说明 |
|---|---|---|
| makeNode() | NodeContainer绑定NodeController时或调用rebuild()时 | 必须重写,构建节点树并返回挂载节点 |
| aboutToResize() | NodeContainer执行Measure时 | 获取节点布局大小,参数为Size对象 |
| aboutToAppear() | NodeContainer触发onAppear()时 | 节点即将显示时的初始化操作 |
| aboutToDisappear() | NodeContainer触发onDisappear()时 | 节点即将消失时的清理操作 |
| onTouchEvent() | NodeContainer收到Touch事件时 | 处理触摸事件交互 |
| rebuild() | 需要刷新节点时手动调用 | 重新构建并更新节点 |
5. 总结与最佳实践
组件动态操作是ArkUI框架中提升性能与灵活性的关键技术,特别适用于:
- 性能敏感场景:需要快速加载和响应的页面
- 动态内容展示:内容布局在运行时确定的场景
- 复杂交互应用:需要频繁更新和调整组件树的应用
最佳实践建议:
- 合理使用预创建机制,充分利用空闲时间
- 对复杂组件树考虑使用FrameNode替代自定义组件
- 精确控制NodeController的生命周期,避免内存泄漏
- 在实际业务中根据内容类型动态选择构建策略
通过掌握组件动态操作技术,开发者可以显著提升HarmonyOS应用的性能和用户体验,实现更加灵活高效的UI开发。