HarmonyOS之状态管理、数据同步

224 阅读3分钟

EC60A216-5AF1-4FD5-AC3A-C3CC69630A2D.png

装饰器@State、@Link、@Prop

  • @State装饰的变量是组件内部的状态数据,当状态数据被修改时,会调用所有组件的build()方式进行UI刷新;
  • @Link装饰的变量可以和父组件的@State变量进行双向的数据绑定;
  • @Prop装饰的变量和父组件的@State变量进行单项绑定,父组件@State变量改变,子组件的@Prop变量会改变,反之子组件的@Prop变量改变父组件不做改变;

小试牛刀:

任务管理列表实现:页面视图顶部是任务进度(同步任务状态)、视图下部分是卡片任务列表(任务的增加/修改状态)

  1. 卡片样式
//卡片
@Styles function Card(){
  .width('90%')
  .margin(15)
  .backgroundColor(Color.White)
  .borderRadius(15)
  .shadow({radius:5,offsetY:5,offsetX:3,color:'#000000'})
}
  1. 任务/状态模型封装

//任务
class Task{
  //任务id
  static id:number = 1
  //任务名称
  name:string = '任务'+(Task.id++).toString()
  //是否完成
  isFinished:boolean = false
}

//任务进度
class TaskStatus{
  finishedCount:number = 0
  totalCount:number = 0
}
  1. 页面构建
//页面入口
@Entry
@Component
struct IndexPage {
  @State status:TaskStatus = new TaskStatus()
  // @Provide status:TaskStatus = new TaskStatus()

  build(){
    Column(){
      //顶部进度
      ProgressTaskView({status:$status})
      // ProgressTaskView()
      //任务列表
      ListTaskView({status:$status})
      // ListTaskView()
    }.backgroundColor('#eeeeee')
  }
}
  1. Component:任务进度、任务列表
//任务进度
@Component
struct ProgressTaskView{
  @Link status:TaskStatus

  build(){
    Column(){
      Row(){
        Text('任务进度:')
          .fontColor('#333333')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)

        Row(){
          Text(this.status.finishedCount.toString())
            .fontColor('#333333')
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
          Text('/'+this.status.totalCount.toString())
            .fontColor('#333333')
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
        }
      }.justifyContent(FlexAlign.SpaceEvenly)
      .width('100%')
    }
    .height('150')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .Card()
  }
}
//任务列表
@Component
struct ListTaskView{
  @Link status:TaskStatus

  @State listData:Task[] = [

  ]

  build(){
    Column(){
      //添加任务
      Row(){
        Button({type:ButtonType.Normal}){
          Text('添加任务')
            .fontColor(Color.White)
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
        }.backgroundColor(Color.Blue)
        .width(150)
        .height(50)
        .borderRadius(25)
        .onClick(()=>{
          console.log('添加任务点击')

          this.listData.push(new Task())
          this.status.totalCount = this.listData.length
          console.log('添加任务点击'+this.listData)
        })
      }.justifyContent(FlexAlign.Center)
      .alignItems(VerticalAlign.Center)

      //任务列表
      List({space:0,initialIndex:0}){
        ForEach(this.listData,(item:Task)=>{
          ListItem(){
            Row(){
              Text(item.name)
                .fontColor('#333333')
                .fontSize(14)
                .fontWeight(FontWeight.Medium)
              Checkbox().onChange((value)=>{
                item.isFinished = value
                this.status.finishedCount = this.listData.filter(item =>item.isFinished).length
              }).select(item.isFinished)
            }.justifyContent(FlexAlign.SpaceBetween)
            .alignItems(VerticalAlign.Center)
            .padding({left:20,right:20})
            .width('100%')
          }.height(60)
          .Card()
          .backgroundColor(item.isFinished?'#eeefff':Color.White)
        })
      }.listDirection(Axis.Vertical)
      .cachedCount(this.listData.length)
    }.layoutWeight(1)
  }
}

@Provide、@Consume

@Provide、@Consume提供跨组件数据状态双向同步,适用于多组件多模块的场景

小试牛刀:

还是上面的例子,我们用@Provide、@Consume来实现

  1. 页面构建调整
@Entry
@Component
struct IndexPage {
  @Provide status:TaskStatus = new TaskStatus()

  build(){
    Column(){
      //顶部进度
      ProgressTaskView()
      //任务列表
      ListTaskView()
    }.backgroundColor('#eeeeee')
  }
}
  1. Component调整

ProgressTaskView组件和ListTaskView组件的@Link status:TaskStatus变量换成@Consume status:TaskStatus,然后Run一下看看实际效果

  1. 与@State、@Link、@Prop区别
  • @Provide、@Consume不需要传参数,可以理解为生产者/消费者模式的通知,内存开销相比较大;

  • 子组件@Prop变量实际上是父组件@State变量的拷贝;

  • 子组件@Link变量实际上和父组件@State变量共用一个内存空间,用法类似C/C++函数传地址的方式&($);

@ObjectLink、@Observed

@ObjectLink、@Observed是可以解决模型嵌套(A模型中含B模型,B模型变量状态改变)或数组元素是复杂模型的情况的状态管理数据同步

小试牛刀

依然使用上面的任务列表,完成的任务与未完成的任务做一个背景区分,需要把List的ListItem拆分出来使用;参数item:Task加上@ObjectLink ,Task对象加上@Observed声明,然后CheckBox状态改变,背景就会同步改变;onTap给List组件同步完成任务数使用;

//ListItem
@Component
struct ItemView{
  index:number
  @ObjectLink item:Task
  onTap:()=>void

  build(){
    Row(){
      Text(this.item.name+this.index.toString())
        .fontColor('#333333')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
      Checkbox().onChange((value)=>{
        this.item.isFinished = value
        this.onTap()
        // this.status.finishedCount = this.listData.filter(item =>item.isFinished).length
      }).select(this.item.isFinished)
    }.justifyContent(FlexAlign.SpaceBetween)
    .alignItems(VerticalAlign.Center)
    .padding({left:20,right:20})
    .width('100%')
    .height(60)
    .Card()
    .backgroundColor(this.item.isFinished?'#eeefff':Color.White)
  }
}

@Styles function Card(){
  .width('90%')
  .margin(15)
  .backgroundColor(Color.White)
  .borderRadius(15)
  .shadow({radius:5,offsetY:5,offsetX:3,color:'#000000'})
}

Gitee:gitee.com/winvsmary/h…