HarmanyOS-ArkUI-状态管理-@Prop@Link@Provide@Consume

本文详细介绍了使用Vue构建一个包含任务列表、进度追踪和状态控制的组件,展示了Class模式、@State、@Link和@Provide/Consume的用法。

使用说明

代码

class Task{
  static id:number = 1
  //任务名称
  name:string = `任务${Task.id++}`
  //任务状态
  finished:boolean = false
}

//样式卡片
@Styles function card(){
  .width('95%')
  .padding(20)
  .backgroundColor(Color.White)
  .borderRadius(15)
  .shadow({radius:6,color:'#1f000000',offsetX:2,offsetY:4})
}

//任务完成样式
@Extend(Text) function finishedTask(){
  .decoration({type:TextDecorationType.LineThrough})
  .fontColor('#B1B2B1')
}

@Entry
@Component
struct ProPage {
  //总任务数量
  @State totalTask:number = 0
  //已完成任务数量
  @State finishTask:number = 0
  build() {
    Column({space:10}){
      //任务进度
      TaskStatistics({finishTask:this.finishTask,totalTask:this.totalTask})
      //新增任务
      TaskList({finishTask:$finishTask,totalTask:$totalTask})
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}

@Component
struct TaskStatistics{
  //总任务数量
  @Prop totalTask:number
  //已完成任务数量
  @Prop finishTask:number
  build(){
    //任务进度
    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)
        }
      }
    }
    .card()
    .margin({top:20,bottom:10})
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}

@Component
struct TaskList {
  //总任务数量
  @Link totalTask:number
  //已完成任务数量
  @Link finishTask:number
  //任务数组
  @State tasks: Task[] = []
  handleTaskChange(){
    //更新任务数量
    this.totalTask = this.tasks.length
    //更新已完成任务数量
    this.finishTask = this.tasks.filter(item => item.finished).length
    //完成的任务变灰色
  }
  build() {
    Column(){
      //任务按钮
      Button('新增任务')
        .width(200)
        .onClick(()=>{
          //新增任务数据
          this.tasks.push(new Task())
          this.handleTaskChange()
        })
      //任务列表
      List({space:10}){
        ForEach(
          this.tasks,
          (item:Task,index)=>{
            ListItem(){
              Row(){
                Text(item.name).fontSize(20)
                Checkbox()
                  .select(item.finished)
                  .onChange( val =>{
                    //更新当前任务状态
                    item.finished = val
                    this.handleTaskChange()
                  })
              }
              .card()
              .justifyContent(FlexAlign.SpaceBetween)
            }
            .swipeAction({end:this.DeleteButton(index)})
          }
        )
      }
      .width('100%')
      .alignListItem(ListItemAlign.Center)
      .layoutWeight(1)
    }
  }
  @Builder DeleteButton(index:number){
    Button(){
      Image($r('app.media.delete'))
        .fillColor(Color.White)
        .width(20)
    }
    .width(40)
    .height(40)
    .type(ButtonType.Circle)
    .backgroundColor(Color.Red)
    .margin({left:5})
    .onClick(()=>{
      this.tasks.splice(index,1)
      this.handleTaskChange()
    })
  }
}

代码框架1

@Entry
@Component
struct ProPage {
  //总任务数量
  @State totalTask:number = 0
  //已完成任务数量
  @State finishTask:number = 0
  build() {
    Column({space:10}){
      //任务进度
      TaskStatistics({finishTask:this.finishTask,totalTask:this.totalTask})
      //新增任务
      TaskList({finishTask:$finishTask,totalTask:$totalTask})
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}

@Component
struct TaskStatistics{
  //总任务数量
  @Prop totalTask:number
  //已完成任务数量
  @Prop finishTask:number
  build(){
    //任务进度
    Row(){.......}
    .card()
    .margin({top:20,bottom:10})
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}


@Component
struct TaskList {
  //总任务数量
  @Link totalTask:number
  //已完成任务数量
  @Link finishTask:number
  //任务数组
  @State tasks: Task[] = []
  build() {
    Column(){......}
}

代码框架2-@State@Link@Prop

class模式

class StatInfo{
  totalTask:number = 0
  //已完成任务数量
  finishTask:number = 0
}
@Entry
@Component
struct ProPage {
  //总任务数量
  @State stat: StatInfo = new StatInfo()
  build() {
    Column({space:10}){
      //任务进度
      TaskStatistics({finishTask:this.stat.finishTask,totalTask:this.stat.totalTask})
      //新增任务
      TaskList({stat: $stat})
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}

@Component
struct TaskStatistics{
  //总任务数量
  @Prop totalTask:number
  //已完成任务数量
  @Prop finishTask:number
  build(){
    //任务进度
    Row(){.....}
    }
    .card()
    .margin({top:20,bottom:10})
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}

@Component
struct TaskList {
  //总任务数量
  @Link stat: StatInfo
  //任务数组
  @State tasks: Task[] = []
  build() {
    Column(){......}
  }
}

代码框架3-@Provide@Consume

适合跨组件时使用

浪费cpu算力,能用@State@Link就尽量不用@Provide@Consume

//任务统计信息
class StatInfo{
  totalTask:number = 0
  //已完成任务数量
  finishTask:number = 0
}

@Entry
@Component
struct ProPage {
  //总任务数量
  @Provide stat: StatInfo = new StatInfo()
  build() {

  }
}

@Component
struct TaskStatistics{
  //总任务数量
  @Consume stat: StatInfo
  build(){
   
  }
}

@Component
struct TaskList {
  //总任务数量
  @Consume stat: StatInfo
  //任务数组
  @State tasks: Task[] = []
  build() {

  }
}

<think>我们正在讨论ArkTS中的状态管理装饰器。用户的问题聚焦于为什么使用@State和@Link时需要参数传递,而使用@Provide和@Consume时不需要。我们需要基于提供的引用来解释原因。 引用[1]指出:@Provide和@Consume用于与后代组件的双向数据同步,实现跨层级传递,摆脱了参数传递机制的束缚。而引用[2]则列出了ArkTS中各种状态管理装饰器,包括@State、@Prop、@Link、@Provide/@Consume等。 分析: 1. @State和@Link的使用场景: - @State:组件内部的状态,通常用于组件内部的数据管理。如果子组件需要访问这个状态,则需要通过参数传递(通常配合@Prop或@Link)将状态传递给子组件。 - @Link:用于父子组件之间的双向数据同步。父组件将一个状态变量通过参数传递给子组件,子组件用@Link装饰器接收,这样两边的数据变化会同步。 因此,使用@State和@Link时,我们必须在父组件中显式地将状态作为参数传递给子组件。例如: ```typescript // 父组件 @Component struct ParentComponent { @State message: string = 'Hello' build() { Column() { ChildComponent({ message: $message }) // 通过参数传递,$message表示传递的是引用(双向绑定) } } } // 子组件 @Component struct ChildComponent { @Link message: string // 通过@Link接收,与父组件的@State建立双向绑定 build() { Text(this.message) .onClick(() => { this.message = 'Hi' }) // 修改会同步到父组件 } } ``` 2. @Provide和@Consume的使用场景: - @Provide:在祖先组件中声明,它提供的状态可以被任意后代组件访问,而不需要通过参数逐层传递。 - @Consume:在后代组件中声明,用于消费祖先组件通过@Provide提供的状态,并且建立双向绑定。 因此,使用@Provide和@Consume时,不需要通过组件的构造函数参数来传递,而是通过ArkTS框架内部建立的跨层级关联。例如: ```typescript // 祖先组件 @Component struct AncestorComponent { @Provide message: string = 'Hello' // 提供状态 build() { Column() { ChildComponent() // 不需要传递参数 } } } // 后代组件(可以是任意层级的子组件) @Component struct ChildComponent { @Consume message: string // 消费祖先提供的状态 build() { Text(this.message) .onClick(() => { this.message = 'Hi' }) // 修改会同步到祖先组件 } } ``` 原因总结: - @State和@Link需要参数传递:因为它们的设计是用于父子组件之间的直接通信,所以必须通过显式的参数传递来建立关联。这种传递是逐级的,即只能从父组件传递给直接子组件。 - @Provide和@Consume不需要参数传递:因为它们的设计目的是跨层级的状态同步,ArkTS框架在后台建立了祖先组件和后代组件之间的直接联系,因此无需通过中间组件一层层传递参数。 此外,引用[1]明确提到:@Provide和@Consume“摆脱参数传递机制的束缚,实现跨层级传递”。 因此,我们可以这样回答用户:使用@State和@Link时,状态传递仅限于父子组件之间,因此必须通过参数传递;而@Provide和@Consume通过ArkTS框架内部机制实现了跨组件层级的直接绑定,因此无需参数传递。 生成相关问题: 1. 如何在ArkTS中实现跨组件层级的状态共享? 2. @Provide和@Consume的工作原理是什么? 3. 在什么情况下应该使用@Link而不是@Consume? </think>在 ArkTS 中,`@State`/`@Link` 需要参数传递而 `@Provide`/`@Consume` 不需要的根本原因在于它们采用了**不同的状态管理机制**,主要体现在以下三方面: ### 1. 作用域与绑定机制不同 - **`@State`/`@Link`** 基于**父子组件的显式参数传递**机制: ```typescript // 父组件显式传递参数 struct Parent { @State message: string = "Hello" build() { Child({ msg: $message }) // 必须通过参数传递 } } // 子组件显式接收参数 struct Child { @Link msg: string // 必须通过参数绑定 } ``` 需要显式通过组件构造函数参数传递,形成父子组件之间的双向绑定[^2]。 - **`@Provide`/`@Consume`** 基于**组件树的隐式上下文注入**机制: ```typescript // 祖先组件提供状态 struct Ancestor { @Provide message: string = "Hello" // 自动注入上下文 build() { Descendant() // 无需参数传递 } } // 任意后代组件消费状态 struct Descendant { @Consume message: string // 自动从上下文获取 } ``` 通过 ArkTS 框架自动建立的跨组件上下文实现状态共享,无需参数传递[^1]。 ### 2. 层级穿透能力不同 - **`@State`/`@Link`** 只能实现**相邻层级**(父子组件)的状态同步。若需跨多层传递,必须在中间组件手动转发参数: ``` 父组件 → (参数) → 子组件 → (参数) → 孙组件 ``` - **`@Provide`/@Consume** 支持**任意层级穿透**,后代组件可直接访问祖先状态: ``` 祖先组件(@Provide) ↘ 子组件(无需传递) ↘ 孙组件(@Consume) ``` 有效避免了"逐层传递参数"的冗余代码[^1]。 ### 3. 设计目的不同 | 特性 | `@State`/`@Link` | `@Provide`/`@Consume` | |---------------------|----------------------------------|-----------------------------------| | **最佳场景** | 父子组件紧密耦合的状态同步 | 跨层级组件解耦的状态共享 | | **参数必要性** | 必须显式传递建立绑定 | 框架自动注入,无需传递 | | **代码侵入性** | 需修改中间组件接口 | 不修改中间组件 | | **典型应用** | 表单控件双向绑定 | 主题/用户信息等全局状态共享 | ### 总结 当需要**父子组件间直接通信**时,使用 `@State`/`@Link` 配合参数传递是合理选择;而当需要**跨多层组件共享状态**时,`@Provide`/`@Consume` 通过框架级上下文注入机制,避免了参数传递的繁琐过程,大幅提升代码可维护性[^1][^2]。 --- **相关问题**: 1. 在什么场景下应该优先选择 `@Provide`/`@Consume` 而不是 `@State`/`@Link`? 2. `@Provide`/`@Consume` 能否替代全局状态管理工具?有什么局限性? 3. 使用 `@Provide`/`@Consume` 时如何避免状态命名冲突? 4. `@Provide`/`@Consume` 的性能开销是否高于参数传递机制? 5. 在类组件和函数式组件中使用 `@Provide`/`@Consume` 有何区别?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值