在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。

状态管理机制

一、@State修饰符

@State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。当这些状态数据被修改时,将会调用所在组件的 build() 方法刷新UI。

@State 状态数据具有以下特征:

  • 支持多种数据类型:Object、class、string、number、boolean、enum类型,以及这些类型的数组。支持Date类型。API11及以上支持Map、Set类型。支持undefined和null类型。API11及以上支持上述支持类型的联合类型,比如string | number, string | undefined 或者 ClassA | null。

    注意:当使用undefined和null的时候,建议显式指定类型,遵循TypeScipt类型校验,比如:@State a : string | undefined = undefiend是推荐的,不推荐@State a: string = undefined。

  • 组件内部私有化:被@State标记过的成员变量只能在组件内部访问。

  • 相互独立:可以重复创建多个实例对象,组件不同实例的内部数据是相互独立的,具有隔离性。
  • 必须初始化:必须为所有@State变量进行初始化。

父组件代码:

  1. import { SubItem } from './SubItem'
  2. // StateTest.ets
  3. @Entry
  4. @Component
  5. struct StateTest{
  6. @State seconds:string = "父组件秒钟数:"+new Date().getSeconds()
  7. build() {
  8. Column({space:20}){
  9. Text(this.seconds)
  10. .backgroundColor(Color.Green)
  11. .fontColor(Color.White)
  12. .fontSize(18)
  13. //两个独立的子组件
  14. SubItem()
  15. SubItem()
  16. Button("点击更新")
  17. .onClick(() => {
  18. this.seconds = "父组件秒钟:" + new Date().getSeconds()
  19. })
  20. }
  21. .width('100%')
  22. .height('100%')
  23. .padding(20)
  24. }
  25. }

子组件代码:

  1. // SubItem.ets
  2. //特别注意:子组件不能用@Entry注解
  3. @Component
  4. export struct SubItem{
  5. @State seconds: string = "子组件秒钟:" + new Date().getSeconds()
  6. build() {
  7. Text(this.seconds)
  8. .fontSize(18)
  9. .backgroundColor(Color.Red)
  10. .fontColor(Color.Orange)
  11. .onClick(() => {
  12. this.seconds = "子组件秒钟:" + new Date().getSeconds()
  13. })
  14. }
  15. }

运行效果图:

@State运行效果图

二、@Prop修饰符

@Prop修饰符用于父子单向同步,是父传子,而子不改父。其特性如下:

  • 内部私有:只在子组件内部有效,被@Prop修饰的变量是私有变量
  • 相互独立:同一子组件可以重复创建实例,每个实例内的数据是私有的,具有隔离性
  • 不支持内部初始化,必须从父组件传值进行组件化
  • 支持的数据类型:Object、class、string、number、boolean、enum类型,以及这些类型的数组。不支持any,支持undefined和null。支持Date类型。

注意 当使用undefined和null的时候,建议显式指定类型,遵循TypeScipt类型校验,比如:@Prop a : string | undefined = undefined是推荐的,不推荐@Prop a: string = undefined。

示例代码:

  1. @Entry
  2. @Component
  3. struct PropTest{
  4. @State seconds:string = new Date().getSeconds().toString()
  5. build() {
  6. Column({space:20}){
  7. Text(`父组件:${this.seconds}`)
  8. .backgroundColor(Color.Orange)
  9. .fontColor(Color.White)
  10. .fontSize(20)
  11. .padding(10)
  12. //解构赋值给子组件
  13. Item({date:this.seconds})
  14. Item({date:this.seconds})
  15. Button('更新时间')
  16. .backgroundColor(Color.Orange)
  17. .fontColor(Color.White)
  18. .fontSize(20)
  19. .onClick(() => {
  20. this.seconds = new Date().getSeconds().toString()
  21. })
  22. }
  23. .width('100%')
  24. .height('100%')
  25. .padding(20)
  26. }
  27. }
  28. @Component
  29. struct Item{
  30. @Prop date:string
  31. build() {
  32. Text(`子组件:${this.date}`)
  33. .backgroundColor(Color.Green)
  34. .fontColor(Color.Orange)
  35. .fontSize(20)
  36. .padding(10)
  37. .onClick(() => {
  38. this.date = new Date().getSeconds().toString()
  39. })
  40. }
  41. }

在点击“更新时间”按钮时,3个控件都呈现同样的文本结果。这说明父组件成功向子组件传递了信息。 父传子

在分别点击两个子组件时,父组件并没有变化,而两个子组件则分别做出不同的文本呈现结果。这说明,@Prop修饰的变量是私有的,并且不会影响父组件。

子组件的UI更新

三、@Link修饰符

@Link修饰符用于父子双向同步。即:父组件数据的更新会通知子组件的变量更新,同时驱动UI更新。@Link修饰符主要有以下特点:

  • 内部私有:只能在组件内部访问
  • 只能通过父组件中的@State变量进行初始化,不能在组件内初始化
  • 支持的数据类型:Object、class、string、number、boolean、enum类型,以及这些类型的数组。支持Date类型。API11及以上支持上述支持类型的联合类型,比如string | number, string | undefined 或者 ClassA | null。

注意:当使用undefined和null的时候,建议显式指定类型,遵循TypeScipt类型校验,比如:@Link a : string | undefined。

  1. @Entry
  2. @Component
  3. struct LinkTest{
  4. @State seconds:string = new Date().getSeconds().toString()
  5. build() {
  6. Column({space:20}){
  7. Text(`父组件:${this.seconds}`)
  8. .backgroundColor(Color.Pink)
  9. .fontColor(Color.White)
  10. .fontSize(18)
  11. .padding(20)
  12. // 此处使用$符号进行初始化,其实使用this.seconds也能实现效果,不知为啥
  13. MyItem({childText: $seconds})
  14. MyItem({childText: $seconds})
  15. Button('更新父组件秒钟数')
  16. .backgroundColor(Color.Red)
  17. .fontColor(Color.White)
  18. .fontSize(18)
  19. .fontWeight(FontWeight.Bold)
  20. .onClick(() => {
  21. this.seconds = new Date().getSeconds().toString()
  22. })
  23. }
  24. .width('100%')
  25. .height('100%')
  26. .padding(50)
  27. }
  28. }
  29. //子组件
  30. @Component
  31. struct MyItem{
  32. // @Link修饰的变量只能被父组件初始化
  33. @Link childText:string
  34. build() {
  35. Text(`子组件:${this.childText}`)
  36. .fontColor(Color.Orange)
  37. .fontSize(18)
  38. .backgroundColor(Color.Green)
  39. .padding(20)
  40. .onClick(() => {
  41. this.childText = new Date().getSeconds().toString()
  42. })
  43. }
  44. }

代码运行效果图:

双向同步

四、@Provide和@Consume:与后代组件双向同步

五、@Watch修饰符

@Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。

  1. //子组件
  2. @Component
  3. struct MyItem{
  4. // @Watch修饰符监听变量的改变,当变量被改变时会执行回调方法
  5. @Watch('onValueChange') @Link childText:string
  6. build() {
  7. Text(`子组件:${this.childText}`)
  8. .fontColor(Color.Orange)
  9. .fontSize(18)
  10. .backgroundColor(Color.Green)
  11. .padding(20)
  12. .onClick(() => {
  13. this.childText = new Date().getSeconds().toString()
  14. })
  15. }
  16. onValueChange(){
  17. console.log(`childText成员变量更新:${this.childText}`)
  18. }

监听变量的更新