鸿蒙应用开发之装饰器大总结 —— 从语法糖到全场景跨语言运行时的全景视角
1. 什么是“装饰器”
装饰器是 “对类、属性、方法进行元编程扩展的声明式语法”。
在 ArkTS 中,装饰器 = “编译期标记 + 运行期拦截”
的双层模型: 编译期:ets-loader 扫描装饰器 → 生成元数据 → 注册到 ComponentMeta; 运行期:由 ArkUI
框架消费元数据,完成状态注册、依赖收集、Diff、最小化更新
先说我们开发中最常用的V1版本和V2版本:
2、装饰器对照表
V1 版本 | V2 版本 | 核心差异 |
---|---|---|
@State | @Local/@Param@Once | V1 的 @State 是数据源,V2 拆分为: • @Local(无需外部初始化) • @Param(支持外部传参) |
@Prop | @Param | V1 的 @Prop 仅支持单向同步,V2 的 @Param 统一父子传参逻辑 |
@Link | @Param@Event | V1 的 @Link 通过框架实现双向同步,V2 通过 @Param 传值 + @Event 触发父组件更新 |
@Observed@Track | @ObservedV2@Trace | V2 支持深度观测嵌套对象属性,无需层层绑定 |
@Provide@Consume | @Provider@Consumer | V2 的 @Provider 和 @Consumer 支持函数类型,且 alias 匹配规则更严格 |
@Watch | @Monitor | V2 的 @Monitor 可监听属性级变化,并支持获取旧值和新值 |
@Component | @ComponentV2 | V2 组件装饰器明确区分输入输出,提升组件独立性 |
2.1关键能力对比
能力维度 | V1 版本 | V2 版本 |
---|---|---|
观察能力 | 仅支持对象第一层变化,需搭配 @Observed 和 @ObjectLink | 支持深度嵌套属性变化监听,@Trace 实现精准观测 |
传参机制 | 父子组件传参需 @Prop、@Link 等不同装饰器 | 统一使用 @Param 简化父子传参逻辑 |
动画支持 | 完全兼容 animateTo | 当前部分场景与 animateTo 存在兼容性问题 |
组件复用 | 支持 @Reusable 装饰器复用组件 | 暂不支持组件复用能力 |
代码复杂度 | 深层嵌套对象更新需逐层绑定 | 直接观测深层属性,代码更简洁 |
2.2、典型场景案例
2.2.1. 父子组件数据同步
- V1 实现:
// 父组件@State parentCount: number = 0;build() {Child({ childCount: $parentCount }) }// 子组件@Link childCount: number;
- V2 实现:
// 父组件@Local parentCount: number = 0;build() {Child({ childCount: this.parentCount })}// 子组件@Param childCount: number;@Event onUpdate: () => void; // 通过事件触发父组件更新
2.2.2. 深度监听对象属性
- V1 实现:需层层绑定 @Observed 和 @ObjectLink。
- V2 实现:
@ObservedV2class User {@Trace name: string = "Alice";}@ComponentV2struct Profile {@Local user: User = new User();build() {Text(this.user.name) // 修改 user.name 自动触发 UI 更新}}
2.3、版本选择建议
- 新项目:优先使用 V2,功能更强大且长期维护。
- 遗留项目:若无需深度监听或复杂传参,可继续使用 V1;复杂场景可混用 V2 的 @ObservedV2 和 @Trace。
- 限制注意:
- V2 暂不支持组件复用(@Reusable)和局部主题定制1。
- V1 和 V2 装饰器禁止混用(如 @ObservedV2 与 @State 共存会编译报错)。
2.4 、V1迁移到V2版本
“状态拆分”:@State → @Local(内部) / @Param(外部)。
“双向拆解”:@Link → @Param + @Event 手动回传。
“深度观测”:@Observed + @Track → @ObservedV2 + @Trace。
“提供消费改名”:@Provide / @Consume → @Provider / @Consumer。
3. ArkTS 原生装饰器全景图
类别 | 装饰器 | 作用域 | 触发时机 | 常见误区 |
---|---|---|---|---|
页面入口 | @Entry | 文件顶层 struct | 路由表注册 | 忘记导致“白屏” |
自定义组件 | @Component/@ComponentV2 | struct | 生成 ComponentMeta 并注册 | 与 @Entry 混用 |
状态变量 | @State | struct 内部字段 | 组件级响应式 | 基础类型才会深度代理 |
链路传递 | @Provide/@Consume | 跨组件树 | 依赖注入 | 同名 key 区分大小写 |
本地缓存 | @StorageLink/@StorageProp | 全局 | 持久化到 Storage | 同步顺序晚于 aboutToAppear |
懒加载 | @Lazy | 数组项 | 仅创建可视节点 | 不可与 @State 数组混用 |
样式复用 | @Styles | 文件顶层 | 编译期抽离公共样式 | 不能带 state |
扩展组件 | @Extend(Component) | 文件顶层 | 生成新的组件类型 | 不支持泛型 |
弹窗 | @CustomDialog | struct | 自动生成 Dialog 控制器 | 必须手动调用 open() |
并发 | @Concurrent | 函数 | TaskPool 调度 | 不能捕获 this |
后台任务 | @BackgroundTask | 函数 | 长时任务保活 | 需申请 ohos.permission.KEEP_BACKGROUND_RUNNING |
3. 生命周期钩子装饰器
@Entry
@Component
struct Index {@State msg: string = 'Hi'aboutToAppear() // 无装饰器,组件实例将创建onPageShow() // 页面可见onPageHide() // 页面不可见aboutToDisappear() // 组件将销毁
}
注意:生命周期函数勿加 async,否则内部同步代码顺序错乱
4. 系统服务宏装饰器(C++)
宏 | 功能 | 使用位置 | 典型错误 |
---|---|---|---|
DECLARE_INTERFACE_DESCRIPTOR(…) | 生成 Binder 唯一令牌 | 接口类内 | 拼写错误导致 IPC_E_INVALID_DESCRIPTOR |
REGISTER_SYSTEM_ABILITY_BY_ID(cls, id, runOnCreate) | 自动注册 SA | 全局 | 重复 id 引起“already registered”崩溃 |
DISALLOW_COPY_AND_MOVE(cls) | 删除拷贝构造 | 类内 | 与 =delete 重复定义 |
OHOS::sptr | 智能指针“装饰” | 变量 | 循环引用忘记 weak_ptr 造成内存泄漏 |
5. UI 范式装饰器执行模型
- 编译期
ets-loader 扫描所有 @Component → 生成 createComponent 工厂函数 - 运行期
- 首次渲染:执行 factory → 生成 ComponentMeta → 创建 Element → 挂载 RenderNode
- 状态变更:@State setter → 通知 ViewModel → diff → 最小化更新 RenderNode
- 销毁期
aboutToDisappear → 递归卸载子 Element → 释放 RenderNode → 回调 onDestroy
6. 性能与最佳实践
- 状态最小化
能用 @Local 就不用 @State,能用 @StorageLink 就不用 EventHub - 样式静态化
@Styles 内容保持纯粹常量,避免写表达式 - 组件扁平化
深层 @Builder 嵌套 >7 层时,首次创建耗时指数上升 - 并发任务
@Concurrent 函数体 < 1 ms 时,线程切换开销反而拖慢 FPS - 内存泄漏
@CustomDialog 控制器未及时 close() 会持有 Page Element,退出页面无法回收
7. 跨语言装饰器
语言 | 装饰器/属性 | 作用 |
---|---|---|
Rust | #[ohos::bindgen] | 自动生成 ArkTS ↔ Rust FFI 胶水层 |
Python | @ohos.native | 把 Python 函数暴露成系统 SA 接口 |
C | attribute((ohos_capability(“xxx”))) | 在 .cap 表中生成能力声明 |
8. 开发中常见的坑
- @Entry 唯一
- @Component 必须
- @State 不存大型数组
- @Provide key 区分大小写
- @Lazy 与 @State 数组二选一
- @Styles 勿带状态
- @Extend 不支持泛型
- @CustomDialog 记得 close
- @Concurrent 勿捕获 this
- 宏装饰器拼写检查大小写
9. 总结
OpenHarmony 装饰器
├─ ArkTS
│ ├─ 页面入口 @Entry
│ ├─ 组件定义 @Component
│ ├─ 状态管理 @State @Provide @Consume @StorageLink @Lazy
│ ├─ 样式复用 @Styles @Extend @Builder
│ ├─ 弹窗 @CustomDialog
│ ├─ 并发 @Concurrent @BackgroundTask
│ └─ 生命周期钩子(无装饰器,但按顺序)
├─ C++ 宏装饰器
│ ├─ 接口 DECLARE_INTERFACE_DESCRIPTOR
│ ├─ 注册 REGISTER_SYSTEM_ABILITY_BY_ID
│ └─ 智能指针 sptr<T>
├─ UI 范式
│ ├─ 编译期生成 ComponentMeta
│ └─ 运行期驱动 RenderNode
└─ 跨语言├─ Rust #[ohos::bindgen]├─ Python @ohos.native└─ C __attribute__((ohos_capability))
关注我,带你步步解读鸿蒙全栈开发。