鸿蒙开发之状态管理@Prop和@Link

一、用法

在父子组件需要进行数据同步的时候,可以通过@Prop和@Link装饰器来做到。在父组件中用@State装饰,在自组件中用@Prop或@Link装饰。

结论:@Prop用于子组件只监听父组件的数据改变而改变,自己不对数据改变

           @Link用于子组件与父组件都会对数据改变,都需要在数据改变的时候发生相应的更新。

二、@Prop和@Link区别

2.1数据同步的类型不同

@Prop的数据是单向传递的,父组件改变能通知子组件,但是子组件改变不能通知父组件。

@Link的数据是双向传递的,父组件改变能通知子组件,子组件的改变也可以通知父组件。

2.2 装饰的变量类型不同

@Prop可以装饰的类型有限

  • 只能支持string、number、boolean、enum类型。
  • 如果父组件是对象类型,可以传递对象的属性,子组件可以内部可以是对象的属性。
  • 不可以是数组、any类型

@Link可以装饰的类型较多

  • 支持string、number、boolen、enum、object、数组等
  • 数组的增、删、改都可以触发更新
  • 嵌套类型以及数组中对象的属性无法触发更新,类似@State
2.3装饰后属性传递方式不同

@Prop装饰的属性不能初始化,因为传递方式是拷贝,从父组件中拷贝一份值给子组件。所以可以直接通过this.xxx传递。

@Link装饰的属性也不能初始化。传递方式是指针传递,需要用$修饰传递的属性。

鸿蒙开发之状态管理@Prop和@Link_第1张图片

三、实际案例

接上一篇state案例,我们在同一个组件中编写的代码,导致代码非常臃肿。那么,为了更加简洁,就对代码进行了拆分,将代码又拆分出来了2个组件TaskStitics(任务进度卡片)和StaticList(任务列表卡片)。

其中,TaskStitics组件只接收数据的改变,不会对数据改变,所以采用单向同步的@Prop装饰

StaticList组件因为可以对数据进行完成、删除等操作,也会对父组件的数据改变,所以采用双向同步的@Link装饰。


class Task {
  static  id: number = 1
  name:string = '任务名称'+Task.id++
  finished:boolean = false
}

@Styles function  card() {
  .width('90%')
  .padding(20)
  .backgroundColor(Color.White)
  .borderRadius(15)
  .shadow({radius:6,color:'#1F00000',offsetX:2,offsetY:4})
}

@Extend(Text) function tasksuccessed(finish: boolean) {
  .decoration({type: finish ? TextDecorationType.LineThrough : TextDecorationType.None})
  .fontColor(finish? '#B1B2B1': Color.Black)
}


@Entry
@Component
struct  ProgressTask {

  @State totalTasks: number = 0
  @State finishTasks:number = 0
  @State tasks: Task[] = []
  
  build() {
    Column() {
      //顶部任务进度卡片
      TaskStitics({ totalTasks: this.totalTasks, finishTasks: this.finishTasks })
      //任务列表+新增按钮
      StaticList({totalTasks:$totalTasks,finishTasks:$finishTasks,tasks:$tasks})
    }
  }
}


@Component
struct TaskStitics {

  @Prop totalTasks: number
  @Prop finishTasks:number

  build() {
    //进度卡片
    Row() {
      Text('任务进度:')
        .fontWeight(FontWeight.Bold)
        .fontSize(30)
        .layoutWeight(1)

      Stack() {
        Progress({value:this.finishTasks,total:this.totalTasks,type:ProgressType.Ring})
          .width(100)

        Row() {
          Text(this.finishTasks.toString())
            .fontSize(24)
            .fontColor('#36D')

          Text(' / '+this.totalTasks.toString())
            .fontSize(24)
        }
      }

    }
    .card()
    .height(150)
    .margin({top:20,bottom:10})
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}

@Component
struct StaticList {

  @Link totalTasks: number
  @Link finishTasks:number
  @Link tasks: Task[]

  handleTaskNumber() {
    this.totalTasks = this.tasks.length
    this.finishTasks = this.tasks.filter(item =>
    item.finished
    ).length
    console.log('完成任务数'+this.finishTasks)
  }

  @Builder deleteButton(index:number) {
    Button('➖')
      .fontColor(Color.White)
      .backgroundColor(Color.Red)
      .width(40)
      .height(40)
      .type(ButtonType.Circle)
      .margin({left:5})
      .onClick(() => {
        this.tasks.splice(index,1)
        this.handleTaskNumber()
      })
  }

  build() {
    Column() {
      //添加按钮
      Button('新增任务')
        .width(200)
        .height(35)
        .onClick(() => {
          this.tasks.push(new Task())
          this.handleTaskNumber()
        })
        .margin({ bottom: 20 })

      //任务列表
      List({ space: 10 }) {
        ForEach(this.tasks, (task: Task, index) => {
          ListItem() {
            Row() {
              Text(task.name)
                .fontSize(20)
                .tasksuccessed(task.finished)

              Checkbox()
                .select(task.finished)
                .onChange(value => {
                  task.finished = value
                  console.log('任务状态' + value + '')
                  this.handleTaskNumber()
                })
            }
            .card()
            .justifyContent(FlexAlign.SpaceBetween)
          }
          .swipeAction({ end: this.deleteButton(index) })
        }, item => '' + item.name)
      }
      .width('100%')
      .alignListItem(ListItemAlign.Center)
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }

}

四、补充@Provide和@Consume

@Provide和@Consume可以跨组件提供类似于@State和@Link的双向同步。

设想有三级组件父组件->子组件->孙子组件。

如果想要父组件与孙子组件中的数据达到双向同步,那么就需要先父组件与子组件绑定,然后子组件与孙子组件绑定,需要绑定多次。这个时候就可以用@Provide和@Consume装饰器了。

在父组件中用@Provide装饰,在孙子组件中采用@Consume装饰,就可以实现双向数据同步。

需要注意的是,使用这两个装饰器的时候不需要在父组件中传递参数。

整体关系如下图

鸿蒙开发之状态管理@Prop和@Link_第2张图片

你可能感兴趣的:(鸿蒙开发,鸿蒙)