@State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。
@State装饰的变量拥有以下特点:
@State装饰的变量与子组件中的@Prop装饰变量之间建立单向数据同步,与@Link、@ObjectLink装饰变量之间建立双向数据同步。
@State装饰的变量生命周期与其所属自定义组件的生命周期相同。
@Prop装饰的变量和父组件建立单向的同步关系:
@Prop变量允许在本地修改,但修改后的变化不会同步回父组件。
当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。因此,数值的同步是父组件到子组件(所属组件),子组件数值的变化不会同步到父组件。
子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。
@Link装饰的变量与其父组件中的数据源共享相同的值。
@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。
其中@Provide装饰的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费(绑定)”祖先组件提供的变量。
@Provide/@Consume装饰的状态变量有以下特性:
@Provide装饰的状态变量自动对其所有后代组件可用,即该变量被“provide”给他的后代组件。由此可见,@Provide的方便之处在于,开发者不需要多次在组件之间传递变量。
后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递。
@Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,建议类型相同,否则会发生类型隐式转换,从而导致应用行为异常。
// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;
// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;
@Provide和@Consume通过相同的变量名或者相同的变量别名绑定时,@Provide装饰的变量和@Consume装饰的变量是一对多的关系。不允许在同一个自定义组件内,包括其子组件中声明多个同名或者同别名的@Provide装饰的变量,@Provide的属性名或别名需要唯一且确定,如果声明多个同名或者同别名的@Provide装饰的变量,会发生运行时报错。
@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:
被@Observed装饰的类,可以被观察到属性的变化;
子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。
单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。
使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。
@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。
@Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为false的情况下,就会触发@Watch的回调。
@Component
struct TotalView {
@Prop @Watch('onCountUpdated') count: number = 0;
@State total: number = 0;
// @Watch 回调
onCountUpdated(propName: string): void {
this.total += this.count;
}
build() {
Text(`Total: ${this.total}`)
}
}
@Entry
@Component
struct CountModifier {
@State count: number = 0;
build() {
Column() {
Button('add to basket')
.onClick(() => {
this.count++
})
TotalView({ count: this.count })
}
}
}
// xxx.ets
@Entry
@Component
struct TextInputExample {
@State text: string = ''
controller: TextInputController = new TextInputController()
build() {
Column({ space: 20 }) {
Text(this.text)
TextInput({ text: $$this.text, placeholder: 'input your word...', controller: this.controller })
.placeholderColor(Color.Grey)
.placeholderFont({ size: 14, weight: 400 })
.caretColor(Color.Blue)
.width(300)
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
}
@Track应用于class对象的属性级更新。@Track装饰的属性变化时,只会触发该属性关联的UI更新。
@Track是class对象的属性装饰器。当一个class对象是状态变量时,@Track装饰的属性发生变化,只会触发该属性关联的UI更新;而未被标记的属性不能在UI中使用。
class LogTrack {
@Track str1: string;
@Track str2: string;
constructor(str1: string) {
this.str1 = str1;
this.str2 = 'World';
}
}
class LogNotTrack {
str1: string;
str2: string;
constructor(str1: string) {
this.str1 = str1;
this.str2 = '世界';
}
}
@Entry
@Component
struct AddLog {
@State logTrack: LogTrack = new LogTrack('Hello');
@State logNotTrack: LogNotTrack = new LogNotTrack('你好');
isRender(index: number) {
console.log(`Text ${index} is rendered`);
return 50;
}
build() {
Row() {
Column() {
Text(this.logTrack.str1) // UINode1
.fontSize(this.isRender(1))
.fontWeight(FontWeight.Bold)
Text(this.logTrack.str2) // UINode2
.fontSize(this.isRender(2))
.fontWeight(FontWeight.Bold)
Button('change logTrack.str1')
.onClick(() => {
this.logTrack.str1 = 'Bye';
})
Text(this.logNotTrack.str1) // UINode3
.fontSize(this.isRender(3))
.fontWeight(FontWeight.Bold)
Text(this.logNotTrack.str2) // UINode4
.fontSize(this.isRender(4))
.fontWeight(FontWeight.Bold)
Button('change logNotTrack.str1')
.onClick(() => {
this.logNotTrack.str1 = '再见';
})
}
.width('100%')
}
.height('100%')
}
}