HarmonyOS鸿蒙开发组件状态管理详细说明(State、Prop、Link、Provide和Consume、Watch、ObjectLink和Observed)

avatar
作者
猴君
阅读量:0

组件状态管理

关注’猿来编码‘,微信订阅号,回复 ’组件状态‘,获取详细


文章目录



一、@State

@State用于装饰当前组件的状态变量,@State装饰的变量在发生变化时,会驱动当前组件的视图刷新,语法如下:

@State count:number = 1; 

需要注意的是:@State装饰的变量必须进行本地初始化。

允许装饰的类型
基本类型:string、number、boolean、enum
对象类型:json对象、class的实例对象、
数组类型:上面所有类型的数组
能够观察到的变化
注意:
并不是状态变量的所有更改都会引起UI的刷新
只有可以被框架观察到的修改才会引起UI刷新
属性本身的改变都可以 (无论什么类型)
对象:能监视对象的直接属性变化,不能监视嵌套属性的改变
数组:能监视数组中元素的变化,不能监视元素对象内部的变化
测试
定义对象和数组的状态和显示数据

@State @Watch('onChange')obj: {a: {b: number}} = {a: {b: 1}} @State @Watch('onChange2')arr: {a: number}[] = [{a: 1}] 
Text('state.obj' + JSON.stringify(this.obj)).fontSize(18) Text('state.arr' + JSON.stringify(this.arr)).fontSize(18) 

修改属性对象

this.obj = {a: {b: 2}}   // 修改属性本身 => 能监视  this.obj.a = {b: 3}  // 修改属性对象的直接属性 =》 能监视  this.obj.a.b = 4   // 修改属性对象的嵌套属性 =》 不能监视到 

修改属性数组

this.arr = [] // 修改属性本身 => 能监视  this.arr[0] = {a: 2} // 修改属性数组的元素 => 能监视  this.arr.push({a: 3}) // 修改属性数组的元素 => 能监视  this.arr[0].a = 4 // 修改属性数组的元素对象内部属性 =》 不能监视 

二、@Prop

@Prop用于装饰子组件的状态变量,@Prop装饰的变量会同步父组件的状态,但只能单向同步,也就是父组件的状态变化会自动同步到子组件,而子组件的变化不会同步到父组件。
父组件

@Entry @Component struct Parent{   @State count:number = 1;   build(){     Column(){       Child({count:this.count});     }   } } 

子组件

@Component export struct Child{   @Prop count:number;   build(){     Text('prop.count: ' + this.count);   } } 

需要注意的是:@Prop装饰的变量不允许本地初始化,只能通过父组件传参进行初始化。
允许装饰的类型
官方文档:只允许基本类型,不允许对象和数组
实际情况:与@State一致,可以是对象、数组
能够观察到的变化
与@State一致

三、@Link

@Link用于装饰子组件的状态变量,@Prop变量同样会同步父组件状态,但是能够双向同步。也就是父组件的变化会同步到子组件,而子组件的变化也会同步到父组件。
父组件

@Entry @Component struct Parent{   @State count:number = 1;   build(){     Column(){       Child({count: $count});     }   } } 

子组件

@Component export struct Child{   @Link count:number;   build(){     Text('link.count: ' + this.count);   } } 

需要注意的是:@Link装饰的变量不允许本地初始化,只能由父组件通过传参进行初始化,并且父组件必须使用$变量名的方式传参,以表示传递的是变量的引用。
允许装饰的类型
与@State一致
框架能够观察到的变化
与@State一致

四、@Provide 与 @Consume

@Provide和@Consume用于跨层级传递状态信息,其中@Provide用于装饰祖先组件的状态变量,@Consume用于装饰后代组件的状态变量。可以理解为祖先组件提供(Provide)状态信息供后代组件消费(Consume),并且祖先和后代的状态信息可以实现双向同步。
注意:
@Provide装饰变量必须本地初始化,而@Consume装饰的变量不允许本地初始化。
@Provide & @Consume处理的状态数据是双向同步的

祖先组件

@Entry @Component struct GrandParent {     @Provide count: number = 1;     @Provide('msg') message: string = '老A';     build() {         Column() {             ...         }     } } 

后代组件

@Entry @Component export struct Child {   	@Consume count: number;     @Consume('msg') childMsg: string;        build() {         Column() {             Text('Consume.count: ' + this.count);           	Text('Consume.childMsg: ' + this.childMsg);         }     } } 

允许装饰的类型
与@State一致
能够观察到的变化
与@State一致
测试:

@Component export default struct Child1 {    @Prop obj1: {a: {b: number}}   @Prop arr1: {a: number}[]    update() {     // this.obj1 = {a: {b: 3}}   // 修改属性本身 => 能监视     // this.obj1.a = {b: 4}  // 修改属性对象的直接属性 =》 能监视     // setTimeout(() => {     //   this.obj1.a.b = 9   // 修改属性对象的嵌套属性 =》 不能监视到 报错(且会报错,导致程序退出)     // }, 1000)      // this.arr1 = [] // 修改属性本身 => 能监视     // this.arr1[0] = {a: 5} // 修改属性数组的元素 => 能监视     this.arr1.push({a: 8}) // 修改属性数组的元素 => 能监视     setTimeout(() => {       this.arr1[0].a = 5 // 修改属性数组的元素对象内部属性 =》 不能监视(且会报错,导致程序退出)     }, 1000)   }    build() {     Column({space: 10}) {       Text('prop.obj' + JSON.stringify(this.obj1)).fontSize(18)       Text('prop.arr' + JSON.stringify(this.arr1)).fontSize(18)       Button('开始更新 prop').onClick(() => this.update())     }     .width('100%')     .padding(20)     .border({width: 1, color: Color.Gray})   } } 
@Component export default struct Child2 {    @Link obj2: {a: {b: number}}   @Link arr2: {a: number}[]    update () {     // this.obj1 = {a: {b: 2}}   // 修改属性本身 => 能监视     // this.obj2.a = {b: 4}  // 修改属性对象的直接属性 =》 能监视     // setTimeout(() => {     //   this.obj2.a.b = 9   // 修改属性对象的嵌套属性 =》 不能监视到     // })      // this.arr2 = [] // 修改属性本身 => 能监视     // this.arr2[0] = {a: 3} // 修改属性数组的元素 => 能监视     this.arr2.push({a: 5}) // 修改属性数组的元素 => 能监视     setTimeout(() => {       this.arr2[0].a = 4 // 修改属性数组的元素对象内部属性 =》 不能监视     })   }    build() {     Column({space: 10}) {       Text('link.obj2' + JSON.stringify(this.obj2)).fontSize(18)       Text('link.arr2' + JSON.stringify(this.arr2)).fontSize(18)        Button('开始更新 link').onClick(() => this.update())     }     .width('100%')     .padding(20)     .border({width: 1, color: Color.Gray})   } } 
import Child1 from './Child1' import Child2 from './Child2'  @Entry @Component struct StateTest {    @State obj: {a: {b: number}} = {a: {b: 1}}   @State arr: {a: number}[] = [{a: 1}]    update() {     // this.obj = {a: {b: 1}}   // 修改属性本身 => 能监视     // this.obj.a = {b: 2}  // 修改属性对象的直接属性 =》 能监视     // setTimeout(() => {     //   this.obj.a.b = 6   // 修改属性对象的嵌套属性 =》 不能监视到     // }, 1000)      // this.arr = [] // 修改属性本身 => 能监视     // this.arr[0] = {a: 2} // 修改属性数组的元素 => 能监视     this.arr.push({a: 3}) // 修改属性数组的元素 => 能监视     setTimeout(() => {       this.arr[0].a = 9 // 修改属性数组的元素对象内部属性 =》 不能监视     }, 1000)   }    build() {     Column({space: 10}) {       Text('state.obj' + JSON.stringify(this.obj)).fontSize(18)       Text('state.arr' + JSON.stringify(this.arr)).fontSize(18)        Button('开始更新2 state').onClick(() => this.update())        Child1({obj1: this.obj, arr1: this.arr})        Child2({obj2: $obj, arr2: $arr})     }     .width('100%')     .padding(20)   } } 

五、@Watch

用来监视状态数据的变化,包括:@State、@Prop、@Link、@Provide、@Consume
一旦状态数据变化,监视的回调就会调用
我们可以在监视的回调中执行应用需要的特定逻辑
以@State为例编码

@State @Watch('onCountChange') count: number = 0  /**  * 一旦count变化,此回调函数就会自动调用  * @param name  被监视的状态属性名  */ onCountChange (name) {   // 可以在此做特定处理 } 

测试

@Component export default struct Child1 {    @Prop count1: number    build() {     Column({space: 10}) {       Row({space: 10}) {         Text('prop.count1: ' + this.count1).fontSize(18)         Button('更新prop.count1').onClick(() => this.count1 += 1)       }     }     .width('100%')     .padding(20)     .border({width: 1, color: Color.Gray})   } } 
@Component export default struct Child2 {    @Link count2: number    build() {     Column({space: 10}) {       Row({space: 10}) {         Text('link.count2: ' + this.count2).fontSize(18)         Button('开始更新link.count2').onClick(() => this.count2 += 1)       }     }     .width('100%')     .padding(20)     .border({width: 1, color: Color.Gray})   } } 
import GrandChild from './GrandChild'  @Component export default struct Child3 {     build() {     Column({space: 10}) {       GrandChild()     }     .width('100%')     .padding(20)     .border({width: 1, color: Color.Gray})   } } 
import promptAction from '@ohos.promptAction' @Component export default struct GrandChild {    @Consume @Watch('onMsgChange') msg: string    onMsgChange () {     promptAction.showToast({message: this.msg})   }    build() {     Column({space: 10}) {       Text('Consume.msg: ' + this.msg).fontSize(18)       Button('开始更新Consume.count2').onClick(() => this.msg += '--')     }     .width('100%')     .padding(20)     .border({width: 1, color: Color.Gray})   } } 
import Child1 from './Child1' import Child2 from './Child2' import promptAction from '@ohos.promptAction' import Child3 from './Child3' @Entry @Component struct StateBaseTest {    @State @Watch('onCountChange')  count: number = 0   @Provide msg: string = 'abc'    /**    * 一旦count变化,此回调函数就会自动调用    * @param name  被监视的状态属性名    */   onCountChange (name) {     if (this.count>3) {       promptAction.showToast({message: `当前count为${this.count},已经超过了3`})     }   }    build() {     Column({space: 10}) {       Row({space: 10}) {         Text('state.count: ' + this.count)           .fontSize(18)         Button('更新state.count').onClick(() => this.count += 1)       }       Text('count值超过3,每次更新都提示一下')         .fontColor(Color.Orange)        Child1({count1: this.count})        Child2({count2: $count})        Divider()        Text('provide.msg: ' + this.msg)         .fontSize(18)       Button('开始更新provide.msg').onClick(() => this.msg += '++')        Child3()     }     .width('100%')     .padding(20)   } } 

六、@ObjectLink 和 @Observed

前面的问题:
● 属性对象中的嵌套对象的属性修改不能监视到,也就不会自动更新UI
● 属性数组中的元素对象的属性修改不能监视到,也就不会自动更新UI
● @Props与@Link声明接收的属性,必须是@State的属性,而不能是@State属性对象中嵌套的属性
解决办法
● 将嵌套对象的类型用class定义, 并使用@Observed来装饰
● 子组件中定义的嵌套对象的属性, 使用@ObjectLink来装饰
测试:

@Observed class Person2 {   id: number;   name: string;   age: number;    constructor(id, name, age) {     this.id = id     this.name = name     this.age = age   } }  @Component struct PersonItem {   // @Prop person: Person   // @Link person: Person   @ObjectLink person: Person2    build() {     Row() {       Text(JSON.stringify(this.person))         .fontSize(20)       Button('更新年龄')         .onClick(() => this.person.age += 2)     }     .border({width: 1, color: Color.Gray})     .padding(10)   } }  @Entry @Component struct PersonList {   @State persons: Person2[] = [     new Person2(1, 'Tom', 12),     new Person2(2, 'Jack', 13),   ]    build() {     Column({space: 10}){       Button('更新嵌套对象的属性:一个人的年龄')         .onClick(() => {           this.persons[0].age++         })       List() {         ForEach(this.persons, (item: Person2, index: number) => {           ListItem(){             PersonItem({person: item})           }         })       }     }     .padding(20)   } }  

在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!