综述
V1稳定版 - 组件装饰器
组件状态管理 | 生效范围 | 声明及初始化 | 允许的变量类型 |
---|---|---|---|
@State | 组件内 | 必须指定类型并初始化 | Object、class、string、number、boolean、enum、Date、Map、Set、Array |
@Prop | 父子单向同步 | 必须指定类型,可本地初始化,也可父组件初始化 若父和本地均为初始化,则undefined |
同@State |
@Link | 父子双向同步 | 必须指定类型,必须父组件初始化,不支持父组件常规变量初始化 | 同@State |
@Provide和@Consume | 跨层级双向数据同步 | @Provide 必须指定类型,可本地也可父组件初始化; @Consume禁止父组件初始化 |
同@State |
@ObjectLink/@Prop和@Observed | 多层嵌套双/单向同步 | @Observed装饰类; @ObjectLink/@Prop装饰变量,接收装饰类的实例 |
@Observed装饰class; @ObjectLink必须指定类型为@Observed装饰类的class,必须父组件初始化,同步源必须为@State,@Link,@Provide,@Consume或者@ObjectLink装饰的数据;不支持简单类型,支持 Date、Map、Set、Array |
组件状态管理 | 观察变化 |
---|---|
@State | string、number、boolean ,可直接观察到; class、object 可观察到 自身赋值、属性赋值;可观察到嵌套对象的赋值,观察不到嵌套对象的属性的赋值(第二层) ; array 可观察到长度的更改,如 自身赋值、增删;可观察到数据项赋值更改,但观察不到 数据项赋值的属性 ; Date可观察到 自身赋值,及系统接口的更改; Map可观察到 自身赋值,及系统接口的更改,如 set clear delete; Set可观察到 自身赋值,及系统接口的更改,如 add clear delete; |
@Prop | string、number、boolean ,可直接观察到; class、object 可观察到 自身赋值、属性赋值;可观察到嵌套对象的赋值,观察不到嵌套对象的属性的赋值(第二层);若应用嵌套场景,需用@Observed装饰,且每一层都要杯 @Prop 接收,可观察到@Prop 装饰的值变化; array 可观察到长度的更改,如 自身赋值、增删;可观察到数据项赋值更改,但观察不到 数据项赋值的属性 ; Date可观察到 自身赋值,及系统接口的更改; Map可观察到 自身赋值,及系统接口的更改,如 set clear delete; Set可观察到 自身赋值,及系统接口的更改,如 add clear delete; |
@Link | 同 @State 可使用@Watch 在双向同步时,触发对应本地方法 |
@Provide和@Consume | 同 @State |
@Observed和@ObjectLink | 父组件数据源是class,可以观察到属性的变化; 数据源是数据,可以观察到属性的变化; 数据源是嵌套,可观察到@ObjectLink装饰类型的,并被@Observed装饰的class属性变化; 对于继承Date、Map、Set 的 class 对象,可观察到整体赋值,及系统接口的更改 |
V2试用版 - 组件装饰器
组件状态管理 | 生效范围 | 声明及初始化 | 允许的变量类型 | 对标V1 |
---|---|---|---|---|
@ComponentV2 | 组件内 | -- | -- | -- |
@Local | 组件内 | 必须组件内部初始化; 可初始化子组件中 @Param 装饰的变量 |
Object、class、string、number、boolean、Date、Map、Set、Array | @State |
@Param | 父到子单向同步 [但更改对象属性会同步] |
可本地初始化,但不允许再次改动 a read-only property(但对象的属性可更改);若本地不初始化,则与@Require装饰器一起用,必须父组件初始化; 可以接受任意类型的数据源,包括普通变量、状态变量、常量、函数返回值等; 可初始化子组件中 @Param 装饰的变量; |
同 @Local | -- |
@Once | 父子仅同步一次,必须搭配@Param使用 |
作为辅助拦截器,无法单独使用,本身没有类型要求,也没有观察能立 可解除 @Param无法在本地修改的限制,且修改能够触发UI刷新 |
-- | -- |
@Event | 方法回调,并更新子组件@Param变量 | 必须用来装饰方法型变量 @Event changeFactory: ()=>void = ()=>{}; | 父组件可多次更新子组件中的@Param | -- |
@Provider和@Consumer | 跨层级双向同步 | @Provider 禁止父组件初始化,必须本地初始化, 支持重载;【即@Provider可以重名,@Consumer向上查找最近的@Provider。】 @Consumer 禁止父组件初始化, 必须本地初始化,找不到@Provider时使用本地默认值 |
number、string、boolean、class、Array、Date、Map、Set,也支持箭头函数 不支持class【搭配@Trace和@ObservedV2使用可支持】 |
V1:@Provide和@Consume ,具体区别,见下方 |
@Monitor | 状态变量修改监听 | @Monitor 装饰的是监听函数 | 在组件中使用时,监听必须是 装饰器@Local、@Param、@Provider、@Comsumer、@Computed装饰的变量; 在@ObservedV2装饰的类中,可使用 @Monitor 搭配 @Trace 使用 |
@Watch |
@Type | 序列号时,不丢失属性的复杂类型 | Object class以及Array、Date、Map、Set等内嵌类型; 只能使用在class中,不支持简单类型 |
-- |
组件状态管理 | 观察变化 | V2版,升级与对比 |
---|---|---|
@Local | string、number、boolean ,可直接观察到; class、object 仅可观察到 自身赋值 变化,对属性的观察依赖 @ObservedV2和@Trace装饰器; 简单类型的数组,可以观察到整体或数组项的变化;对象数组时,无法观察到对象属性的变化; Date、Map、Set、Array 时,可观察到整体赋值,和系统方法带来的变化 |
|
@Param | string、number、boolean ,可直接观察到; 类对象,仅能观测到整体的改变;对属性的观察依赖 @ObservedV2和@Trace装饰器; 简单数组,可观测到整体,及数据线的改变;嵌套类或对象数组时,无法观察对象属性的变化; Date、Map、Set、Array 时,可观察到整体赋值,和系统方法带来的变化 |
-- |
@Once | -- | 可解除 @Param无法在本地修改的限制,且修改能够触发UI刷新 |
@Event | 子组件中定义@Event的方法,父组件中实现此方法; 子组件调用方法,会触发父组件的回调,更改父组件的值,这些值更改会同步到对应的子组件@Param 变量中,并触发刷新 |
-- |
@Monitor | @Monitor监听的变量需要被@Local、@Param、@Provider、@Consumer、@Computed装饰; @Monitor 可以同时监听多个状态变量 @Monitor("message", "name"); @ObservedV2 装饰的类中, 使用@Monitor监听被@Trace装饰的属性; 监听深层属性变化时,该属性需要被@Trace修饰; 【对象整体改变,但监听的属性不变时,不触发@Monitor回调。】 在继承类场景下,可以在继承链中对同一个属性进行多次监听。; @Monitor无法监听内置类型(Array、Map、Date、Set)的API调用引起的变化; 支持对数组中的项进行监听,包括多维数组,对象数组 |
可用获得变化之前的值 |
详解
V1稳定版
@State
1,初始化范围
- 支持父组件初始化,支持父组件中常规变量初始化(不会触发UI刷新),支持父组件中所有状态器修饰变量初始化(会触发UI刷新)
-
可初始化子组件中,常规变量、@State、@Link、@Prop、@Provide。
2,在构造函数中,状态变量的修改不会触发UI更改
3,不允许在build中,改变状态变量,循环渲染,可能造成ANR
build() {
Text(`${this.message++}`)
}
It's illegal to change @Component state while build (initial render or re-render) is on-going
@Prop
1,初始化范围
- 支持父组件初始化,支持父组件中常规变量初始化(不会触发UI刷新),支持父组件中所有状态器修饰变量初始化(会触发UI刷新)
-
可初始化子组件中,常规变量、@State、@Link、@Prop、@Provide。
2,父组件的 @State属性值更改时,会直接覆盖掉子组件中的本地修改
3,建议@Prop深度嵌套数据不要超过5层,嵌套太多会导致深拷贝占用的空间过大,引起性能问题,更建议使用@ObjectLink
@Link
1,初始化范围
- 禁止本地初始化,必须父组件状态器修饰变量初始化,支持父组件中所有状态器修饰变量(会触发UI刷新)
-
可初始化子组件中,常规变量、@State、@Link、@Prop、@Provide。
2,@Link 不能在 @Entry 中使用
3,@Link 需要严格保证该变量,与数据源类型完全相同
4,数据源@State 位置放在build函数后定义,编译会报错
@Provide/@Consume
1,@Provide初始化
- 可本地初始化,也可父组件常规变量、状态器修饰变量初始化
-
可初始化子组件中,@State、@Link、@Prop、@Provide。
@Consume初始化
- 禁止本地和父组件初始化,仅支持 同名/别名的@Provide初始化
-
可初始化子组件中,@State、@Link、@Prop、@Provide。
2,可通过相同的变量名,或变量别名绑定。建议类型相同,否早会进行隐式转换
// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;
// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;
3, @Provide 和 @Consume 是一对多的关系。同一组件,包括其子组件,不允许存在多个 同名或同别名的@Provide变量
4,@Provide 支持 allowOverride 重写。
在组件的子组件下,可使用 allowOverride 重写同名@Provide
struct Parent(){
@Provide("nums") reviewNum: number = 40;
}
struct Son(){
@Provide({ allowOverride: "nums" }) reviewNum: number = 30;
}
struct Child(){
@Consume("nums") reviewNum: number = 10;
}
@Observed和@ObjectLink
1,@ObjectLink初始化范围
- 禁止本地初始化,类型必须是@Observed装饰的class 或数组;必须父组件状态器修饰变量初始化,父组件的数据源,其class 和数组必须是@State,@Link,@Provide,@Consume或者@ObjectLink装饰的数据。
2,@ObjectLink 不能在 @Entry 中使用
3,允许@ObjectLink装饰的变量,的属性赋值,不允许对变量本身赋值
this.classA.name = 'test' //OK
this.classA=new ClassA() //wrong
//这是不允许的,对于实现双向数据同步的@ObjectLink,
//赋值相当于要更新父组件中的数组项或者class的属性,这是不能实现的
4,class的构造函数中,修改变量,不会触发刷新
V2 试用版
@ComponentV2
1,暂不支持组件复用、LocalStorage等
2,限制
- 非状态变量,不接受父组件传递
- V1 中的状态装饰器修饰的所有变量,不允许传递给 V2 中的装饰器变量;反向 V2 向V1传递时,不支持Array、Set、Map、Date类型
- V1中不允许使用V2的装饰器;反过来同理
- V2 的装饰器,不允许装饰被 @Observed 装饰的类
@Local
@State 升级版
2,如上所示,观察嵌套类、对象数组的所有变化,推荐使用 @Local 搭配 @ObservedV2与@Trace
3,当 多次将同一个复杂类型的常量 , 赋值给 @Local 时,每次都会触发更新。
因为,@Trace、@Local装饰的Date、Map、Set、Array,会添加一层代理来观察变化。 再次赋值时,装饰变量会变成 Proxy 类型,判断不相等。
可用 UIUtils.getTarget() 判断两者是否相同
//错误使用,每次都会更新
list: string[][] = [['a'], ['b'], ['c']];
@Local dataObjFromList: string[] = this.list[0];
....onClick(() => {
// 新值和本地初始化的值相同
this.dataObjFromList = this.list[0];
})
//修改后方法
....onClick(() => {
// 获取原始对象来和新值做对比
if (UIUtils.getTarget(this.dataObjFromList) !== this.list[0]) {
this.dataObjFromList = this.list[0];
}
})
@Param
1,对于复杂类型如类对象,@Param会接受数据源的引用。在组件内可以修改类对象中的属性,该修改会同步到数据源。
@Once
1,@Once装饰器仅在变量初始化时接受外部传入值进行初始化,当后续数据源更改时,不会将修改同步给子组件:
- @Once必须搭配@Param使用,单独使用或搭配其他装饰器使用都是不允许的。
- @Once不影响@Param的观测能力,仅针对数据源的变化做拦截。
- @Once与@Param装饰变量的先后顺序不影响实际功能。
- @Once与@Param搭配使用时,可以在本地修改@Param变量的值。
2,使用@Param @Once相当于使用@Local,区别在于@Param @Once能够接受外部传入初始化
@Event
1,子组件调用方法,触发父组件的回调是立即生效的;
但父组件再回调中更改值,将变化同步回子组件的过程是异步的
2,代码示例
@ComponentV2
struct EventView{
@Local childName:string = 'Marry'
build() {
EventChild({name:this.childName,method:(str:string) => {
this.childName = str
}})
}
}
@ComponentV2
struct EventChild{
@Param name:string = 'Marry'
@Event method:(str:string)=>void
build() {
Column(){
Text(this.name)
Button('@Param change') .onClick(() => this.method('change to Jack'))
}
}
}
@Provider和@Consumer
1,注意区分,V1 中的 @Provide和@Consume 和 V2 中的 @Provider和@Consumer
2,V1版本和V2版本区别
3,@Provider和@Consumer 本身不支持class,若需要修饰此类型,则需要配合 @Trace 使用
@ComponentV2
struct PCView{
@Provider('clazz') c:PCClass = new PCClass()
build() {}
}
@ObservedV2
class PCClass{
@Trace name:string = 'PCClass'
}
@Monitor
1,@Monitor VS @Watch
2,属性变化判断的根据是,严格相等(===);当一次事件多次改变同一个属性时,使用初始值和最终值进行判断
3,单个@Monitor [] ,可用检测多个属性变化
4,搭配 @ObservedV2和@Trace ,可实现深度监听
5,不建议在一个类中对同一个属性进行多次@Monitor的监听。当一个类中存在对一个属性的多次监听时,只有最后一个定义的监听方法会生效
6,避免在@Monitor中再次更改被监听的属性,这会导致无限循环
7,@Monitor 可以获得改变前和改变后的值
@Monitor('clazz.name') onNameChange(monitor: IMonitor){
let before = monitor.value()?.before as string
let now = monitor.value()?.now as string
}