鸿蒙V2组件状态装饰器(对比V1组件)
本文对比分析了鸿蒙V1与V2组件状态装饰器的核心差异。V2新增了@Local(内部状态)、@Event(子传父回调)等专属装饰器,强化了状态管理能力。主要变化包括:@Local禁止外部初始化,@Param取代@Prop并禁止子组件修改,@Event标准化子传父逻辑,@Provider/@Consumer强制别名匹配。V1到V2的迁移路径为:@State→@Local、@Prop→@Param、@L
一、背景
目前项目基于鸿蒙V2组件开发,而之前项目是以V1组件为主,所以对V2组件状态装饰器的使用限制或与V1的核心差异认知较浅;为快速掌握V2组件状态管理逻辑,本文结合鸿蒙官方文档和实际开发场景,梳理 V2 组件核心状态装饰器的用法、特性,并对比 V1 组件的差异,形成参考
二、状态装饰器对比
|
装饰器 |
核心能力 |
V1 对应装饰器 |
核心差异(V2 vs V1) |
|
@Local |
组件内部只读状态(禁止外部初始化) |
@State |
V1 @State 允许外部初始化,V2 @Local 禁止 |
|
@Param |
父传子单向只读状态 |
@Prop |
V2 @Param 禁止子组件修改(编译报错),V1 @Prop 允许本地修改 |
|
@Once |
配合 @Param 解绑父组件绑定 |
无 |
V1无,V2专属,V2 用于实现 @Param 本地可修改 |
|
@Event |
子传父回调函数 |
无(需自定义回调) |
V1无,V2专属,V2 标准化子传父逻辑 |
|
@Provider |
跨组件层级提供状态 |
@Provide |
V2 强制别名匹配、禁止外部初始化、支持重载 |
|
@Consumer |
跨组件层级消费状态 |
@Consume |
V2 必须本地初始化(兜底默认值)、支持函数类型 |
三、详细解析
3.1、组件内部状态:@Local(V2 专属)
定义:组件内部的私有状态变量,具备 “变化观察能力”,完全禁止外部初始化 / 修改,仅能在组件内部初始化和更新。
与V1组件@State对比:
|
用法 |
@State |
@Local |
|
参数 |
无。 |
无。 |
|
从父组件初始化 |
可选。 |
不允许外部初始化。 |
|
观察能力 |
能观测变量本身以及一层的成员属性,无法深度观测。 |
能观测变量本身,深度观测依赖@Trace装饰器。 |
|
数据传递 |
可以作为数据源和子组件中状态变量同步。 |
可以作为数据源和子组件中状态变量同步。 |
@Entry
@ComponentV2
struct Index {
// 仅内部初始化,外部无法修改/传入
@Local count: number = 0;
build() {
Column() {
Text(`当前计数:${this.count}`)
// 内部可修改,触发UI刷新
Button('+1').onClick(() => this.count++)
// 传递给子组件@Param
Child({ num: this.count })
}
}
}
3.2、父传子单向状态:@Param
定义:父组件向子组件传递的单向只读状态,子组件禁止直接修改,仅能通过 @Event 通知父组件更新,或搭配 @Once 解绑后本地修改。
与V1组件@Prop和@Link对比
|
维度 |
V2 @Param |
V1 @Prop |
V1 @Link |
|
传递方向 |
单向(父→子) |
单向(父→子) |
双向(父↔子) |
|
子组件修改权限 |
禁止(编译报错) |
允许修改本地副本(不影响父,不会同步给父) |
允许修改(同步父组件) |
|
本地初始化 |
支持(兜底默认值) |
支持 |
不支持 |
|
刷新触发 |
仅父改触发子刷新 |
父改触发子刷新 / 子改仅自身 |
父子互改互相触发刷新 |
@Param + @Once用法
定义:@Once 让 @Param 仅初始化同步一次父值,之后解绑为子组件本地普通变量,可自由修改(与父组件解耦)。
// 子组件
@ComponentV2
struct Child {
// 仅初始化同步父值,后续可本地修改
@Once @Param num: number = 0;
build() {
Column() {
Text(`子组件数值:${this.num}`)
// 允许本地修改(仅子组件生效)
Button('子组件修改').onClick(() => this.num++)
}
}
}
3.3、子传父回调:@Event(V2 专属)
定义:子组件向父组件传递数据 / 事件的标准化回调函数,配合 @Param 实现 “可控式双向通信”(替代 V1 @Link)
同步机制:子组件调用 @Event 后,父组件值立刻更新,但子组件 @Param 同步为异步(渲染前生效);
与V1对比:V1无此装饰器,需要自定义回调函数
3.4、跨组件状态共享:@Provider + @Consumer
定义:跨多层组件的状态共享方案(替代 V1 同名装饰器),@Provider 提供状态,@Consumer 消费状态,核心通过 “别名(alias)” 匹配。
|
特性 |
V2 @Provider/@Consumer |
V1 @Provide/@Consume |
|
@Consumer 初始化 |
必须本地初始化(找不到 @Provider 时用默认值) |
API20 前禁止初始化(找不到抛异常),API20 后支持默认值 |
|
支持类型 |
支持 function 类型 |
不支持 function 类型 |
|
观察能力 |
观测自身赋值变化,深度观测需 @Trace |
观测第一层变化,深度观测需 @Observed/@ObjectLink |
|
别名匹配 |
alias 是唯一匹配 key(默认用变量名) |
优先匹配 alias,匹配不到可匹配变量名 |
|
@Provider 初始化 |
禁止外部初始化 |
允许外部初始化 |
|
重载支持 |
默认开启(就近匹配) |
默认关闭(需配置 allowOverride) |
// 父组件:提供状态
@Entry
@ComponentV2
struct Index {
// 未显式指定alias,默认用变量名'shareData'
@Provider() shareData: string = '全局共享数据';
build() {
Column() {
Child()
}
}
}
// 子组件:消费状态
@ComponentV2
struct Child {
// 指定alias='shareData',匹配父组件@Provider
@Consumer('shareData') data: string = '兜底默认值';
build() {
Text(`共享数据:${this.data}`) // 显示“全局共享数据”
}
}
备注:如果没有@Provider(),会直接显示@Consumer定义的默认值
四、V1 —> V2 迁移总结
1、内部状态:V1 @State → V2 @Local(禁止外部初始化,只能组件内部初始化)
2、父传子:V1 @Prop → V2 @Param(禁止子组件修改,支持本地初始化)
3、双向通信:V1 @Link → V2 @Param + @Event(显示回调)
4、跨组件共享:V1 @Provide/@Consume → V2 @Provider/@Consumer(强制别名匹配)
5、本地修改父传值:V1@Prop直接修改 → V2需@Param + @Once
更多推荐




所有评论(0)