HarmonyOS鸿蒙开发组件状态管理详细说明(State、Prop、Link、Provide和Consume、Watch、ObjectLink和Observed)
HarmonyOS鸿蒙开发组件状态管理详细说明(State、Prop、Link、Provide和Consume、Watch、ObjectLink和Observed)
组件状态管理
关注’猿来编码‘,微信订阅号,回复 ’组件状态‘,获取详细
一、@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)
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)