当前位置:   article > 正文

鸿蒙Harmony(八)ArkUI--状态管理器之@Prop 和 @Link & @Provide 和 @Consume & @ObjectLink 和@Observed_鸿蒙@link

鸿蒙@link

承接@State的状态管理器学习

@Prop 和 @Link

当父子组件之间需要数据同步时,可以使用@Prop和@Link装饰器。

@Prop@Link
同步类型单向同步双向同步
允许装饰的变量类型@Prop只支持string、number、boolean、enum类型 ,父组件对象类型,自组件对象属性不可以是数组、any父子类型一致:string、number、boolean,enum,object,class以及他们的数组。数组中元素增、删、替换会引起刷新。嵌套类型以及数组中的对象属性无法触发页面更新
初始化方式不允许子组件初始化父组件传递,禁止子组件初始化

代码示例

在这里插入图片描述

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

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}

@Entry
@Component
struct TaskPage {
  // 总任务数量
  @State totalTask: number = 0
  @State finishTask: number = 0
  @State taskList: Array<Task> = []

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView({ totalTask: this.totalTask, finishTask: this.finishTask })
      // 2.任务列表  @Link 因为传递的是引用,必须使用$ 
      TaskListView({ totalTask: $totalTask, finishTask: $finishTask, taskList: $taskList })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  // @Prop:禁止初始化,单向变更,父组件变动,引起子组件刷新
  @Prop totalTask: number
  @Prop finishTask: number

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.finishTask, type: ProgressType.Ring, total: this.totalTask }).width(120)
        Text(this.finishTask + "/" + this.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Link totalTask: number
  @Link finishTask: number
  @Link taskList: Array<Task>

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            this.TaskItemView(item)
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder TaskItemView(task: Task) {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (task.isDone) {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(task.isDone).onChange((isChecked) => {
        task.isDone = isChecked
        this.finishTask = this.taskList.filter(item => item.isDone).length
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.finishTask = this.taskList.filter(item => item.isDone).length
      this.totalTask = this.taskList.length
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

将totalTask与finishedTask封装为对象依旧生效

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

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}
// 封装为对象
class StatInfo {
  totalTask: number = 0
  finishTask: number = 0
}

@Entry
@Component
struct TaskPage {
  @State stat: StatInfo = new StatInfo()

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView({ totalTask: this.stat.totalTask, finishTask: this.stat.finishTask })
      // 2.任务列表  @Link 因为传递的是引用,必须使用$
      TaskListView({ stat: $stat })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  // @Prop:禁止初始化,单向变更,父组件变动,引起子组件刷新
  @Prop totalTask: number
  @Prop finishTask: number

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.finishTask, type: ProgressType.Ring, total: this.totalTask }).width(120)
        Text(this.finishTask + "/" + this.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Link stat: StatInfo
  @State taskList: Array<Task> = []

  handleStatistics() {
    this.stat.finishTask = this.taskList.filter(item => item.isDone).length
    this.stat.totalTask = this.taskList.length
  }

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.stat.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            this.TaskItemView(item)
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder TaskItemView(task: Task) {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (task.isDone) {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(task.isDone).onChange((isChecked) => {
        task.isDone = isChecked
        this.handleStatistics()
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.handleStatistics()
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

@Provide和@Consume

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

代码示例

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

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}

class StatInfo {
  totalTask: number = 0
  finishTask: number = 0
}

@Entry
@Component
struct TaskPage {
  // @Provide 跨组件传递且被@Provide装饰器修饰的组件不需要被传递
  @Provide stat: StatInfo = new StatInfo()

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView()
      // 2.任务列表  @Link 因为传递的是引用,必须使用$
      TaskListView()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  @Consume stat: StatInfo

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.stat.finishTask, type: ProgressType.Ring, total: this.stat.totalTask }).width(120)
        Text(this.stat.finishTask + "/" + this.stat.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Consume stat: StatInfo
  @State taskList: Array<Task> = []

  handleStatistics() {
    this.stat.finishTask = this.taskList.filter(item => item.isDone).length
    this.stat.totalTask = this.taskList.length
  }

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.stat.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            this.TaskItemView(item)
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder TaskItemView(task: Task) {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (task.isDone) {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(task.isDone).onChange((isChecked) => {
        task.isDone = isChecked
        this.handleStatistics()
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.handleStatistics()
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

@ObjectLink 和@Observed

@ObjectLink 和@Observed 装饰器用于在涉及嵌套对象或数组元素为对象的场景中进行双向数据同步。

使用说明

1.嵌套对象使用@Observed 修饰,比如下面

@Observed
class Task {
  static id: number = 1
  name: string = '任务' + Task.id++
  isDone: boolean = false
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.需要根据嵌套对象属性的变更而刷新视图的组件,通过传入参数且该参数需要被@ObjectLink装饰

@Component
struct TaskItemView {
  @ObjectLink task: Task
  onTaskChange: () => void

  build() {}
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

代码示例

实现点击后任务名称样式变更,如下图所示,

在这里插入图片描述

@Observed
class Task {
  static id: number = 1
  name: string = '任务' + Task.id++
  isDone: boolean = false
}

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}

class StatInfo {
  totalTask: number = 0
  finishTask: number = 0
}

@Entry
@Component
struct TaskPage {
  // @Provide 跨组件传递且被@Provide装饰器修饰的组件不需要被传递
  @Provide stat: StatInfo = new StatInfo()

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView()
      // 2.任务列表  @Link 因为传递的是引用,必须使用$
      TaskListView()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  @Consume stat: StatInfo

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.stat.finishTask, type: ProgressType.Ring, total: this.stat.totalTask }).width(120)
        Text(this.stat.finishTask + "/" + this.stat.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Consume stat: StatInfo
  @State taskList: Array<Task> = []

  handleStatistics() {
    this.stat.finishTask = this.taskList.filter(item => item.isDone).length
    this.stat.totalTask = this.taskList.length
  }

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.stat.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            // 变更,传入函数引用
            TaskItemView({ task: item,onTaskChange:this.handleStatistics.bind(this) })
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.handleStatistics()
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

@Component
struct TaskItemView {
  @ObjectLink task: Task
  onTaskChange: () => void

  build() {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (this.task.isDone) {
        Text(this.task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(this.task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(this.task.isDone).onChange((isChecked) => {
        this.task.isDone = isChecked
        this.onTaskChange()
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/256814
推荐阅读
相关标签
  

闽ICP备14008679号