HarmonyOS鸿蒙开发组件状态管理详细说明

654 篇文章 4 订阅
648 篇文章 6 订阅

组件状态管理

一、@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)
  }
}

以上就是常用组件状态说明

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→鸿蒙生态应用开发白皮书V2.0PDF

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学些啥?

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:鸿蒙 (Harmony OS)开发学习手册

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值