1、@Prop@Link@Provide@Consume装饰器图解

这里我们用一张完整的图来汇整@Prop@Link 的作用,如下所示:

在这里插入图片描述
这里我们还需要注意的时传递数据的限制。整理如下图:
在这里插入图片描述
注意
@Link 传参是地址引用,所以参数前面用 $修饰,不是 this 修饰。
@Provide@Consume 成对使用,@Provide负组件使用,@Consume子组件使用,不需要显示传参。

技巧总结

  • 什么时候用@prop : 子组件对父组件传递的数据无更改时
  • 什么时候用@Link : 子组件对父组件传递的数据有更改时

2、案例演示

1、模块划分

上节内容,我们讲解了一个任务统计的功能实现,对任务统计功能进行模块划分其实就分成两个部分,上部分,任务统计模块,下部分,任务列表模块,如下所示:

在这里插入图片描述

2、模块封装

划分好了对应的模块,我们就可以对相应的模块进行代码封装。这里分成 任务统计模块和列表管理模块

1、任务统计模块

代码如下所示:

@Component
struct TaskStatistics {

  //任务总数
  @Prop totalTask: number
  //任务完成数
  @Prop finishTask: number

  build() {
    //1、任务进度卡片
    Row() {
      Text('任务进度:')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      //重叠容器
      Stack() {
        Progress({
          value: this.finishTask,
          total: this.totalTask,
          type: ProgressType.Ring
        })
        Row() {
          Text(this.finishTask.toString())
            .fontSize(24)
            .fontColor('#36D')
          Text(' / ' + this.totalTask.toString())
            .fontSize(24)
        }
      }

    }
    .cardBackground()
    .margin({ top: 20, bottom: 10 })
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}

2、列表管理模块

代码如下所示:


@Component
struct CreateTaskList {

  //任务总数
  @Link totalTask: number
  //任务完成数
  @Link finishTask: number

  //任务列表
  @State taskList: Task[] = []

  CalculateTask() {
    //1、更新任务总数量
    this.totalTask = this.taskList.length
    //2、更新已完成的任务数量
    this.finishTask = this.taskList.filter(item => item.finished).length
  }

  build() {
    Column() {
      //2、新增任务按钮
      Button('新增任务')
        .width(200)
        .onClick(() => {
          //1、新增任务数据
          this.taskList.push(new Task())
          //2、更新任务总数量
          this.CalculateTask()
        })

      //3、任务列表显示
      List({ space: 10 }) {
        ForEach(
          this.taskList,
          (item: Task, id) => {
            ListItem() {
              Row() {
                if (item.finished) {
                  Text(item.name)
                    .fontSize(20)
                    .finishedTaskStyle()
                } else {
                  Text(item.name)
                    .fontSize(20)
                }
                Checkbox()
                  .select(item.finished)
                  .onChange(val => {
                    //1、更改任务状态
                    item.finished = val
                    //2、任务完成数量变更
                    this.CalculateTask()
                  })

              }
              .cardBackground()
              .justifyContent(FlexAlign.SpaceBetween)
            }
            .swipeAction({ end: this.DeleteButton(id) })
          }
        )

      }
      .width('100%')
      .layoutWeight(1)
      .alignListItem(ListItemAlign.Center)

    }
  }

  @Builder DeleteButton(index: number) {
    Button() {
      Image($r('app.media.task_delete'))
        .fillColor(Color.White)
        .width(20)
    }
    .width(40)
    .height(40)
    .type(ButtonType.Circle)
    .backgroundColor(Color.Red)
    .margin(5)
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.CalculateTask()
    })
  }
}

3、主界面

主界面引用子组件代码如下:


@Entry
@Component
struct TaskPage {
  //任务总数
  @State totalTask: number = 0
  //任务完成数
  @State finishTask: number = 0

  build() {
    Column({ space: 10 }) {

      //1、任务统计
      TaskStatistics({ finishTask: this.finishTask, totalTask: this.totalTask })
      //2、任务李彪
      CreateTaskList({ finishTask: $finishTask, totalTask: $totalTask })

    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}

是不是发现页面一下子简洁了,这就是封装带来的效果,简化代码结构,提高代码可读性。

4、完整代码

class Task {
  //静态任务Id,静态变量方便任务名称编号获取,且为所有内部属性共享的一个变量
  static id: number = 1
  //任务名称
  name: string = '任务' + Task.id++
  //任务完成状态
  finished: boolean = false
}

//样式构件 任务卡片样式
@Styles function cardBackground() {
  .width('95%')
  .padding(20)
  .backgroundColor(Color.White)
  .borderRadius(15)
  .shadow({ radius: 0, color: '#1F000000', offsetX: 2, offsetY: 4 })
}

//任务完成样式 字体灰色,字体中划线删除效果
@Extend(Text) function finishedTaskStyle() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}


@Entry
@Component
struct TaskPage {
  //任务总数
  @State totalTask: number = 0
  //任务完成数
  @State finishTask: number = 0

  build() {
    Column({ space: 10 }) {

      //1、任务统计
      TaskStatistics({ finishTask: this.finishTask, totalTask: this.totalTask })
      //2、任务李彪
      CreateTaskList({ finishTask: $finishTask, totalTask: $totalTask })

    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}


@Component
struct TaskStatistics {

  //任务总数
  @Prop totalTask: number
  //任务完成数
  @Prop finishTask: number

  build() {
    //1、任务进度卡片
    Row() {
      Text('任务进度:')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      //重叠容器
      Stack() {
        Progress({
          value: this.finishTask,
          total: this.totalTask,
          type: ProgressType.Ring
        })
        Row() {
          Text(this.finishTask.toString())
            .fontSize(24)
            .fontColor('#36D')
          Text(' / ' + this.totalTask.toString())
            .fontSize(24)
        }
      }

    }
    .cardBackground()
    .margin({ top: 20, bottom: 10 })
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}


@Component
struct CreateTaskList {

  //任务总数
  @Link totalTask: number
  //任务完成数
  @Link finishTask: number

  //任务列表
  @State taskList: Task[] = []

  CalculateTask() {
    //1、更新任务总数量
    this.totalTask = this.taskList.length
    //2、更新已完成的任务数量
    this.finishTask = this.taskList.filter(item => item.finished).length
  }

  build() {
    Column() {
      //2、新增任务按钮
      Button('新增任务')
        .width(200)
        .onClick(() => {
          //1、新增任务数据
          this.taskList.push(new Task())
          //2、更新任务总数量
          this.CalculateTask()
        })

      //3、任务列表显示
      List({ space: 10 }) {
        ForEach(
          this.taskList,
          (item: Task, id) => {
            ListItem() {
              Row() {
                if (item.finished) {
                  Text(item.name)
                    .fontSize(20)
                    .finishedTaskStyle()
                } else {
                  Text(item.name)
                    .fontSize(20)
                }
                Checkbox()
                  .select(item.finished)
                  .onChange(val => {
                    //1、更改任务状态
                    item.finished = val
                    //2、任务完成数量变更
                    this.CalculateTask()
                  })

              }
              .cardBackground()
              .justifyContent(FlexAlign.SpaceBetween)
            }
            .swipeAction({ end: this.DeleteButton(id) })
          }
        )

      }
      .width('100%')
      .layoutWeight(1)
      .alignListItem(ListItemAlign.Center)

    }
  }

  @Builder DeleteButton(index: number) {
    Button() {
      Image($r('app.media.task_delete'))
        .fillColor(Color.White)
        .width(20)
    }
    .width(40)
    .height(40)
    .type(ButtonType.Circle)
    .backgroundColor(Color.Red)
    .margin(5)
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.CalculateTask()
    })
  }
}

3、父组件是对象@Prop可以是对象属性

1、案例改造

这里我们可以拿上面任务案例进行改造,上面任务统计在父组件中是两个变量,我们把它改造成一个对象,我们用 TaskInfo 类来定义,代码如下:


class TaskInfo{
  //任务总数
  totalTask: number = 0
  //任务完成数
  finishTask: number = 0
}

任务统计模块只是数据的显示,所以在组件内部基本无改动,只是在父组件的调用方式有所变化,如下所示:

  //1、任务统计
      TaskStatistics({ finishTask: this.taskInfo.finishTask, totalTask: this.taskInfo.totalTask })

列表管理模块涉及到信息变化,所以改动较多

  • 变量定义 ---------修改 变更为对象定义 @Link taskInfo: TaskInfo
  • 任务计算 --------- this.totalTask 变更为 this.taskInfo.totalTask;this.totalTask 变更为 this.taskInfo.totalTask
  • 界面引用传参----------- 变成对象传递CreateTaskList({ taskInfo: $taskInfo })
  //任务总数
  @Link taskInfo: TaskInfo

  CalculateTask() {
    //1、更新任务总数量
    this.taskInfo.totalTask = this.taskList.length
    //2、更新已完成的任务数量
    this.taskInfo.totalTask = this.taskList.filter(item => item.finished).length
  }

2、完整代码

class Task {
  //静态任务Id,静态变量方便任务名称编号获取,且为所有内部属性共享的一个变量
  static id: number = 1
  //任务名称
  name: string = '任务' + Task.id++
  //任务完成状态
  finished: boolean = false
}

//样式构件 任务卡片样式
@Styles function cardBackground() {
  .width('95%')
  .padding(20)
  .backgroundColor(Color.White)
  .borderRadius(15)
  .shadow({ radius: 0, color: '#1F000000', offsetX: 2, offsetY: 4 })
}

//任务完成样式 字体灰色,字体中划线删除效果
@Extend(Text) function finishedTaskStyle() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}


class TaskInfo{
  //任务总数
  totalTask: number = 0
  //任务完成数
  finishTask: number = 0
}



@Entry
@Component
struct TaskPage {
  @State taskInfo:TaskInfo = new TaskInfo()

  build() {
    Column({ space: 10 }) {

      //1、任务统计
      TaskStatistics({ finishTask: this.taskInfo.finishTask, totalTask: this.taskInfo.totalTask })
      //2、任务列表
      CreateTaskList({ taskInfo: $taskInfo })

    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}


@Component
struct TaskStatistics {

  //任务总数
  @Prop totalTask: number
  //任务完成数
  @Prop finishTask: number

  build() {
    //1、任务进度卡片
    Row() {
      Text('任务进度:')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      //重叠容器
      Stack() {
        Progress({
          value: this.finishTask,
          total: this.totalTask,
          type: ProgressType.Ring
        })
        Row() {
          Text(this.finishTask.toString())
            .fontSize(24)
            .fontColor('#36D')
          Text(' / ' + this.totalTask.toString())
            .fontSize(24)
        }
      }

    }
    .cardBackground()
    .margin({ top: 20, bottom: 10 })
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}


@Component
struct CreateTaskList {

  //任务总数
  @Link taskInfo: TaskInfo


  //任务列表
  @State taskList: Task[] = []

  CalculateTask() {
    //1、更新任务总数量
    this.taskInfo.totalTask = this.taskList.length
    //2、更新已完成的任务数量
    this.taskInfo.finishTask = this.taskList.filter(item => item.finished).length
  }

  build() {
    Column() {
      //2、新增任务按钮
      Button('新增任务')
        .width(200)
        .onClick(() => {
          //1、新增任务数据
          this.taskList.push(new Task())
          //2、更新任务总数量
          this.CalculateTask()
        })

      //3、任务列表显示
      List({ space: 10 }) {
        ForEach(
          this.taskList,
          (item: Task, id) => {
            ListItem() {
              Row() {
                if (item.finished) {
                  Text(item.name)
                    .fontSize(20)
                    .finishedTaskStyle()
                } else {
                  Text(item.name)
                    .fontSize(20)
                }
                Checkbox()
                  .select(item.finished)
                  .onChange(val => {
                    //1、更改任务状态
                    item.finished = val
                    //2、任务完成数量变更
                    this.CalculateTask()
                  })

              }
              .cardBackground()
              .justifyContent(FlexAlign.SpaceBetween)
            }
            .swipeAction({ end: this.DeleteButton(id) })
          }
        )

      }
      .width('100%')
      .layoutWeight(1)
      .alignListItem(ListItemAlign.Center)

    }
  }

  @Builder DeleteButton(index: number) {
    Button() {
      Image($r('app.media.task_delete'))
        .fillColor(Color.White)
        .width(20)
    }
    .width(40)
    .height(40)
    .type(ButtonType.Circle)
    .backgroundColor(Color.Red)
    .margin(5)
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.CalculateTask()
    })
  }
}

4、@Provide@Consume案例

1、案例改造

我们继续对上面任务统计功能进行改造,用来演示 @Provide@Consume。

我们把主页面 TaskPage 中的

  @State taskInfo:TaskInfo = new TaskInfo() 

修改为

  @Provide taskInfo:TaskInfo = new TaskInfo()

把子组件 CreateTaskList 中的

  //任务总数
  @Link taskInfo: TaskInfo

修改为

 //任务总数
  @Consume taskInfo: TaskInfo

子组件 TaskStatistics 修改如下

  • 变量定义修改为对象 @Consume taskInfo: TaskInfo
  • 数据操作从对象中获取数据 value: this.taskInfo.finishTask
@Component
struct TaskStatistics {

  //任务总数
  @Consume taskInfo: TaskInfo

  build() {
    //1、任务进度卡片
    Row() {
      Text('任务进度:')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      //重叠容器
      Stack() {
        Progress({
          value: this.taskInfo.finishTask,
          total: this.taskInfo.totalTask,
          type: ProgressType.Ring
        })
        Row() {
          Text(this.taskInfo.finishTask.toString())
            .fontSize(24)
            .fontColor('#36D')
          Text(' / ' + this.taskInfo.totalTask.toString())
            .fontSize(24)
        }
      }

    }
    .cardBackground()
    .margin({ top: 20, bottom: 10 })
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}


主界面改造,没有了参数传递

在这里插入图片描述

2、完整代码

class Task {
  //静态任务Id,静态变量方便任务名称编号获取,且为所有内部属性共享的一个变量
  static id: number = 1
  //任务名称
  name: string = '任务' + Task.id++
  //任务完成状态
  finished: boolean = false
}

//样式构件 任务卡片样式
@Styles function cardBackground() {
  .width('95%')
  .padding(20)
  .backgroundColor(Color.White)
  .borderRadius(15)
  .shadow({ radius: 0, color: '#1F000000', offsetX: 2, offsetY: 4 })
}

//任务完成样式 字体灰色,字体中划线删除效果
@Extend(Text) function finishedTaskStyle() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}


class TaskInfo{
  //任务总数
  totalTask: number = 0
  //任务完成数
  finishTask: number = 0
}



@Entry
@Component
struct TaskPage {
  @Provide taskInfo:TaskInfo = new TaskInfo()

  build() {
    Column({ space: 10 }) {

      //1、任务统计
      TaskStatistics()
      //2、任务列表
      CreateTaskList()

    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}


@Component
struct TaskStatistics {

  //任务总数
  @Consume taskInfo: TaskInfo

  build() {
    //1、任务进度卡片
    Row() {
      Text('任务进度:')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      //重叠容器
      Stack() {
        Progress({
          value: this.taskInfo.finishTask,
          total: this.taskInfo.totalTask,
          type: ProgressType.Ring
        })
        Row() {
          Text(this.taskInfo.finishTask.toString())
            .fontSize(24)
            .fontColor('#36D')
          Text(' / ' + this.taskInfo.totalTask.toString())
            .fontSize(24)
        }
      }

    }
    .cardBackground()
    .margin({ top: 20, bottom: 10 })
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}


@Component
struct CreateTaskList {

  //任务总数
  @Consume taskInfo: TaskInfo


  //任务列表
  @State taskList: Task[] = []

  CalculateTask() {
    //1、更新任务总数量
    this.taskInfo.totalTask = this.taskList.length
    //2、更新已完成的任务数量
    this.taskInfo.finishTask = this.taskList.filter(item => item.finished).length
  }

  build() {
    Column() {
      //2、新增任务按钮
      Button('新增任务')
        .width(200)
        .onClick(() => {
          //1、新增任务数据
          this.taskList.push(new Task())
          //2、更新任务总数量
          this.CalculateTask()
        })

      //3、任务列表显示
      List({ space: 10 }) {
        ForEach(
          this.taskList,
          (item: Task, id) => {
            ListItem() {
              Row() {
                if (item.finished) {
                  Text(item.name)
                    .fontSize(20)
                    .finishedTaskStyle()
                } else {
                  Text(item.name)
                    .fontSize(20)
                }
                Checkbox()
                  .select(item.finished)
                  .onChange(val => {
                    //1、更改任务状态
                    item.finished = val
                    //2、任务完成数量变更
                    this.CalculateTask()
                  })

              }
              .cardBackground()
              .justifyContent(FlexAlign.SpaceBetween)
            }
            .swipeAction({ end: this.DeleteButton(id) })
          }
        )

      }
      .width('100%')
      .layoutWeight(1)
      .alignListItem(ListItemAlign.Center)

    }
  }

  @Builder DeleteButton(index: number) {
    Button() {
      Image($r('app.media.task_delete'))
        .fillColor(Color.White)
        .width(20)
    }
    .width(40)
    .height(40)
    .type(ButtonType.Circle)
    .backgroundColor(Color.Red)
    .margin(5)
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.CalculateTask()
    })
  }
}

3、总结

本节主要介绍了 @Prop@Link@Provide@Consume 四种装饰器的使用效果以及使用场景。用实际的案例改造实现不同装饰器的效果。

Logo

一站式 AI 云服务平台

更多推荐