状态修饰器
在鸿蒙上,状态装饰器用于修饰状态变量,当状态变量改变时,根据不同的状态装饰器进行相应的响应式UI刷新
各状态装饰器
@State
(1)被@State修饰的变量作用域是组件内部;
(2)其修饰的变量生命周期和其自定义组件的生命周期相同;
(3)必须进行初始化
(4)当装饰器修饰的是基本类型、对象时,数据的变化可以被装饰器发现。当修饰数组时,数组的增删可以被观察,但是数组内某个对象的属性被修改时,不会引起刷新
@Prop
父组件传值给子组件,如果父组件中的值修改时,子组件的变量还是用@State修饰,并不是引起UI的刷新。这时需要对子组件中的状态变量使用@Prop修饰。需要注意的是,被@Pro修饰的状态变量,在进入到后台时,即使值变化也无法进行页面的渲染。
子组件的修改不能同步到父组件!
@Entry
@Component
struct PropDemoPage{
@State name :string= "张三";
build() {
Row(){
childView({ childName:this.name} )
Button("修改name")
.onClick(()=>{
this.name = "李四"
})
}
}
}
@Component
struct childView{
// 通过父组件传值
@Prop childName:string = "";
build() {
Text(this.childName)
}
}
子组件中被@Prop修饰的状态变量名字可以不用和父组件中被@State修饰的状态变量相同
当点击按钮修改name时,父组件中的name会变成李四,并同步到子组件展示:
@Link
@Prop(子组件) + @State(父组件) 只能实现父到子的同步刷新。如果想实现父子的双向同步更新,需要使用@link(子组件) + @State(父组件)。
实现一个效果:
-
点击“父组件传值给子组件”按钮,修改父组件中name的值,并传递到子组件,刷新子组件的UI:子组件从父组件获取的值:XX;
-
子组件输入框中,输入的文本能够同步给父组件,刷新父组件的UI:父组件输入框输入的值:XXXXX.
需要注意,输入框中childName,必须使用引用传递,而不是值传递,引用传递指向同一个地址,修改其中的值,才能够进行同步!!
@Entry
@Component
struct LinkDemoPage{
@State name :string= "张三";
build() {
Column(){
childView({ childName:this.name} )
Button("父组件传值给子组件")
.onClick(()=>{
this.name = "李四"
})
Text("父组件输入框输入的值:" + this.name);
}
}
}
@Component
struct childView{
// 被Link修饰的状态变量,不能被初始化
@Link childName:string;
build() {
Column() {
Text("子组件从父组件获取的值 : " + this.childName)
TextInput({text:$$this.childName}).backgroundColor(Color.Pink)
}.backgroundColor(Color.Gray)
}
}
在输入框输入hello harmony ,父子组件均能刷新:
@Consumer 和 @Provider
我们用@Link就可以直线父子之间的同步。但是如果从父节点到子组件的路劲很长,例如父组件的状态变量值变化,需要同步到孙子的孙子的孙子组件,就需要一层层的传递,较为麻烦。这种场景就可以使用@Provider和@Consumer。
其中:
@Provide 用于修饰父组件中的状态变量;@Consumer用于修饰子组件中的状态变量。并且被这一对修饰的状态变量是双向同步的,变量的名字必须一致(除非只用别名)
例子如下:
@Entry
@Component
struct ProviderDemoPage{
@Provide name :string= "张三";
build() {
Column(){
childView()
Button("父组件传值给子组件")
.onClick(()=>{
this.name = "李四"
})
Text("父组件输入框输入的值:" + this.name);
}
}
}
@Component
struct childView{
// 被Consumer修饰的状态变量,不能被初始化
@Consume name:string;
build() {
Column() {
Text("子组件1从父组件获取的值 : " + this.name)
Text("子组件2从父组件获取的值 : " + this.name)
TextInput({text:$$this.name})
.backgroundColor(Color.Pink)
}.backgroundColor(Color.Gray)
}
}
初始化渲染时,子组件从父组件拿到name的值是”张三”,并且渲染到页面。当点击父组件的按钮,name的值变为”李四”,会同步刷线子组件中所有值。当子组件的输入框输入时,又会将输入框的值同步给父组件
@Provider 和 @Consumer是生产者和消费者模式.
@Observer 和 @ObjectLink
无论是@State和@Prop配对的父到子单向同步,还是@State和@Link的父子双向同步,或者是@Provider和@Consumer的父子、子孙之间的双向同步,都存在一个问题,同步的数据只能是顶层对象,如果顶层对象的属性也是对象,那么非顶层对象的变化,是不会在父子之间进行同步的。例如,一个学生数字,包含了一个学生对象,如果这个学生对象的名字改变了,通过以上的修饰符是不能进行同步的,也就不能解决嵌套对象刷新的问题。此时就需要使用@Observer 和 @ObjectLink进行修饰
其中:
@Observer:修饰最终变化的类,例如上述例子中应该修饰学生累;@ObjectLink用在子组件中,修饰被观察的类的对象,上述例子中就应该是修饰学生的对象。
@Entry
@Component
struct ProviderDemoPage{
@State students :[Student]= [new Student("张三",10)];
build() {
Column(){
childView({student: this.students[0]})
Button("父组件传值给子组件")
.onClick(()=>{
let student:Student = this.students[0];
student.name = "王五";
})
}
}
}
@Component
struct childView{
// 被Consumer修饰的状态变量,不能被初始化
@ObjectLink student:Student;
build() {
Column() {
Text("子组件从父组件获取的值 : " + this.student.name);
TextInput({text:$$this.student.name})
.backgroundColor(Color.Pink)
}.backgroundColor(Color.Gray)
}
}
@Observed
class Student{
constructor(name: string, age: number) {
this.name = name;
this._age = age;
}
private _name: string = "张三";
public set name(value: string) {
this._name = value;
}
public get name(): string {
return this._name;
}
private _age: number = 0;
public set age(value: number) {
this._age = value;
}
public get age(): number {
return this._age;
}
}