HarmonyOS NEXT应用开发之@Provide装饰器和\@Consume装饰器:与后代组件双向同步

@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。

其中@Provide装饰的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费(绑定)”祖先组件提供的变量。

说明:
从API version 9开始,这两个装饰器支持在ArkTS卡片中使用。

概述

@Provide/@Consume装饰的状态变量有以下特性:

  • @Provide装饰的状态变量自动对其所有后代组件可用,即该变量被“provide”给他的后代组件。由此可见,@Provide的方便之处在于,开发者不需要多次在组件之间传递变量。

  • 后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递。

  • @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,建议类型相同,否则会发生类型隐式转换,从而导致应用行为异常。

// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;

// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;

@Provide和@Consume通过相同的变量名或者相同的变量别名绑定时,@Provide装饰的变量和@Consume装饰的变量是一对多的关系。不允许在同一个自定义组件内,包括其子组件中声明多个同名或者同别名的@Provide装饰的变量,@Provide的属性名或别名需要唯一且确定,如果声明多个同名或者同别名的@Provide装饰的变量,会发生运行时报错。

装饰器说明

@State的规则同样适用于@Provide,差异为@Provide还作为多层后代的同步源。

@Provide变量装饰器 说明
装饰器参数 别名:常量字符串,可选。
如果指定了别名,则通过别名来绑定变量;如果未指定别名,则通过变量名绑定变量。
同步类型 双向同步。
从@Provide变量到所有@Consume变量以及相反的方向的数据同步。双向同步的操作与@State和@Link的组合相同。
允许装饰的变量类型 Object、class、string、number、boolean、enum类型,以及这些类型的数组。
支持Date类型。
API11及以上支持Map、Set类型。
支持类型的场景请参考 观察变化 。
API11及以上支持上述支持类型的联合类型,比如string | number, string | undefined 或者 ClassA | null,示例见 @Provide_and_Consume支持联合类型实例 。
注意
当使用undefined和null的时候,建议显式指定类型,遵循TypeScript类型校验,比如:@Provide a : string | undefined = undefined是推荐的,不推荐@Provide a: string = undefined

支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。
不支持any。
必须指定类型。
@Provide变量的@Consume变量的类型必须相同。
被装饰变量的初始值 必须指定。
支持allowOverride参数 允许重写,只要声明了allowOverride,则别名和属性名都可以被Override。示例见@Provide支持allowOverride参数。

@Consume变量装饰器 说明
装饰器参数 别名:常量字符串,可选。
如果提供了别名,则必须有@Provide的变量和其有相同的别名才可以匹配成功;否则,则需要变量名相同才能匹配成功。
同步类型 双向:从@Provide变量(具体请参见@Provide)到所有@Consume变量,以及相反的方向。双向同步操作与@State和@Link的组合相同。
允许装饰的变量类型 Object、class、string、number、boolean、enum类型,以及这些类型的数组。
支持Date类型。
支持类型的场景请参考 观察变化 。
API11及以上支持上述支持类型的联合类型,比如string | number, string | undefined 或者 ClassA | null,示例见@Provide_and_Consume支持联合类型实例。
注意
当使用undefined和null的时候,建议显式指定类型,遵循TypeScript类型校验,比如:@Consume a : string | undefined

支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。
不支持any。
必须指定类型。
@Provide变量和@Consume变量的类型必须相同。
@Consume装饰的变量,在其父组件或者祖先组件上,必须有对应的属性和别名的@Provide装饰的变量。
被装饰变量的初始值 无,禁止本地初始化。

变量的传递/访问规则说明

@Provide传递/访问 说明
从父组件初始化和更新 可选,允许父组件中常规变量(常规变量对@Prop赋值,只是数值的初始化,常规变量的变化不会触发UI刷新,只有状态变量才能触发UI刷新)、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp装饰的变量装饰变量初始化子组件@Provide。
用于初始化子组件 允许,可用于初始化@State、@Link、@Prop、@Provide。
和父组件同步 否。
和后代组件同步 和@Consume双向同步。
是否支持组件外访问 私有,仅可以在所属组件内访问。

图1 @Provide初始化规则图示

image.png

@Consume传递/访问 说明
从父组件初始化和更新 禁止。通过相同的变量名和alias(别名)从@Provide初始化。
用于初始化子组件 允许,可用于初始化@State、@Link、@Prop、@Provide。
和祖先组件同步 和@Provide双向同步。
是否支持组件外访问 私有,仅可以在所属组件内访问

图2 @Consume初始化规则图示

观察变化和行为表现

观察变化

  • 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。

  • 当装饰的数据类型为class或者Object的时候,可以观察到赋值和属性赋值的变化(属性为Object.keys(observedObject)返回的所有属性)。

  • 当装饰的对象是array的时候,可以观察到数组的添加、删除、更新数组单元。

  • 当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。

@Component
struct CompD {
   
   
  @Consume selectedDate: Date;

  build() {
   
   
    Column() {
   
   
      Button(`child increase the day by 1`)
        .onClick(() => {
   
   
          this.selectedDate.setDate(this.selectedDate.getDate() + 1)
        })
      Button('child update the new date')
        .margin(10)
        .onClick(() => {
   
   
          this.selectedDate = new Date('2023-09-09')
        })
      DatePicker({
   
   
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
        selected: this.selectedDate
      })
    }
  }
}

@Entry
@Component
struct CompA {
   
   
  @Provide selectedDate: Date = new Date('2021-08-08')

  build() {
   
   
    Column() {
   
   
      Button('parent increase the day by 1')
        .margin(10)
        .onClick(() => {
   
   
          this.selectedDate.setDate(this.selectedDate.getDate() + 1)
        })
      Button('parent update the new date')
        .margin(10)
        .onClick(() => {
   
   
          this.selectedDate = new Date('2023-07-07')
        })
      DatePicker({
   
   
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
       
<think>嗯,用户之前已经问过关于20种常用装饰器的问题,特别是提到了HarmonyOS/ArkUI中的状态管理装饰器。现在他们希望更详细地了解这几个装饰器及其用法。我需要确保回答的结构清晰,分点说明每个装饰器的作用、使用场景以及示例代码。 首先,我需要回顾之前的回答,确认每个装饰器的基本信息是否正确,是否有遗漏或需要更新的地方。用户提到的装饰器包括@State、@Prop、@Link、@Watch、@Provide/@Consume、@Observed@ObjectLink。这些都属于ArkUI框架的状态管理部分,用于数据驱动UI更新,并且需要在组件内声明。 接下来,我需要详细展开每个装饰器的用途、使用方法注意事项。例如,@State用于组件内部状态,而@Prop用于父组件向子组件单向传递数据。需要解释它们之间的区别,比如@Prop是单向的,子组件不能修改,而@Link是双向的,修改会同步到父组件。@Watch用于监听变量变化,执行回调函数,这在需要处理状态变化后的逻辑时很有用。@Provide@Consume类似于React的Context,用于跨层级组件共享数据,可能需要说明它们的使用场景如何配对使用。 然后,@Observed@ObjectLink用于处理嵌套对象的更新,这里需要强调当对象属性变化时,如何通过装饰器让ArkUI检测到变化并更新UI。可能需要给出一个示例,说明如何用@Observed装饰类,然后用@ObjectLink在组件中绑定该类的实例属性。 在写作过程中,要确保每个部分都有清晰的标题,比如分点介绍每个装饰器,每个装饰器下面有用途说明、用法示例、注意事项等。代码示例要符合ArkUI的语法,可能需要使用TypeScript或ArkTS的语法结构,但用户之前的回答中代码示例是Python风格的,可能需要调整,但根据用户提供的上下文,之前的回答中代码示例可能混合了不同语言的风格,需要确认。不过根据HarmonyOS/ArkUI的文档,应该使用ArkTS(TypeScript的超集)的语法。 另外,用户可能对每个装饰器的实际应用场景感兴趣,需要举一些实际的例子,比如表单输入、组件间通信等。还要注意避免技术术语过于复杂,保持解释的易懂性,特别是如果用户可能是初学者的话。 需要检查是否有遗漏的装饰器,用户列出的七个装饰器是否全部涵盖。同时,是否需要比较不同装饰器之间的区别,比如@State@Link的区别,或者@Prop@Link的区别,以帮助用户更好地选择使用哪个装饰器。 最后,总结部分需要概括这些装饰器的共同目的,即实现数据驱动UI,以及如何根据不同的数据流需求选择合适的装饰器。可能还需要提到最佳实践,比如何时使用@Provide/@Consume而不是逐层传递props,或者如何处理复杂对象的状态管理。 确保回答结构清晰,每个装饰器独立成节,代码示例正确,并且注意事项部分能帮助用户避免常见错误。同时,语言要口语化,但保持专业性,符合用户要求的中文回答。</think>以下是 **HarmonyOS/ArkUI** 中状态管理装饰器的详细说明,涵盖核心用途、代码示例及使用场景: --- ### 一、 **@State** #### 用途 - **组件私有状态**:用于管理组件内部的状态变量,当状态变化时自动触发UI更新。 - **适用场景**:按钮点击计数、表单输入控制等组件内部状态管理。 #### 用法示例 ```typescript @Entry @Component struct MyComponent { @State count: number = 0 // 定义私有状态 build() { Column() { Text(`Count: ${this.count}`) Button('+1') .onClick(() => { this.count++ // 修改@State变量,触发UI刷新 }) } } } ``` #### 注意事项 - **初始化必须赋值**:`@State`变量需在声明时初始化(如`@State value: number = 0`)。 - **不可跨组件传递**:仅限当前组件内部使用。 --- ### 二、 **@Prop** #### 用途 - **单向数据流**:从父组件接收数据,子组件不能修改该值。 - **适用场景**:父组件向子组件传递只读数据(如配置参数)。 #### 用法示例 ```typescript // 子组件 @Component struct ChildComponent { @Prop message: string // 从父组件接收数据 build() { Text(this.message) } } // 父组件 @Entry @Component struct ParentComponent { @State parentMessage: string = "Hello from Parent" build() { Column() { ChildComponent({ message: this.parentMessage }) // 传递数据 Button('Change Parent Message') .onClick(() => { this.parentMessage = "Updated" // 父组件修改后,子组件@Prop自动更新 }) } } } ``` #### 注意事项 - **不可反向修改**:子组件尝试修改`@Prop`变量会触发编译错误。 --- ### 三、 **@Link** #### 用途 - **双向绑定**:组件共享状态,子组件修改会同步到父组件。 - **适用场景**:表单输入双向同步组件间协同编辑。 #### 用法示例 ```typescript // 子组件 @Component struct ChildComponent { @Link inputText: string // 双向绑定父组件数据 build() { TextInput({ text: this.inputText }) .onChange((value: string) => { this.inputText = value // 修改@Link变量,父组件数据同步更新 }) } } // 父组件 @Entry @Component struct ParentComponent { @State text: string = "" build() { Column() { Text(`Parent Text: ${this.text}`) ChildComponent({ inputText: $text }) // 使用$符号传递引用 } } } ``` #### 注意事项 - **传递需用`$`符号**:父组件需通过`$variable`传递引用(如`$text`)。 --- ### 四、 **@Watch** #### 用途 - **监听状态变化**:当装饰的变量被修改时,触发回调函数。 - **适用场景**:数据变化后执行副作用(如保存到本地存储、发送网络请求)。 #### 用法示例 ```typescript @Entry @Component struct MyComponent { @State count: number = 0 @Watch('onCountChange') // 监听count变化,触发onCountChange @State watchedCount: number = 0 // 回调函数 onCountChange() { console.log(`Count changed to: ${this.count}`) } build() { Button('Add') .onClick(() => { this.count++ }) } } ``` #### 注意事项 - **需配合其他装饰器使用**:如`@State`或`@Link`。 --- ### 五、 **@Provide / @Consume** #### 用途 - **跨层级数据共享**:祖先组件通过`@Provide`提供数据,后代组件通过`@Consume`获取,无需逐层传递。 - **适用场景**:主题切换、全局用户信息传递。 #### 用法示例 ```typescript // 祖先组件 @Entry @Component struct AncestorComponent { @Provide theme: string = "dark" // 提供数据 build() { Column() { ChildComponent() } } } // 后代组件 @Component struct ChildComponent { @Consume theme: string // 消费数据 build() { Text(`Current Theme: ${this.theme}`) .fontColor(this.theme === "dark" ? Color.White : Color.Black) } } ``` #### 注意事项 - **作用域内生效**:需在同一组件树层级中。 --- ### 六、 **@Observed @ObjectLink** #### 用途 - **嵌套对象监听**:`@Observed`装饰类,`@ObjectLink`绑定该类的实例属性,实现嵌套属性变化的UI更新。 - **适用场景**:对象内部属性变化(如`user.name`)。 #### 用法示例 ```typescript // 标记可观察的类 @Observed class User { name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } } // 父组件 @Entry @Component struct ParentComponent { @State user: User = new User("Alice", 30) build() { Column() { ChildComponent({ user: this.user }) Button('Change Name') .onClick(() => { this.user.name = "Bob" // 修改嵌套属性,触发子组件更新 }) } } } // 子组件 @Component struct ChildComponent { @ObjectLink user: User // 绑定被@Observed类的实例 build() { Text(`Name: ${this.user.name}`) } } ``` #### 注意事项 - **必须成对使用**:`@ObjectLink`必须绑定被`@Observed`装饰的类实例属性。 --- ### 七、 **选择装饰器的决策逻辑** | 场景 | 推荐装饰器 | |---------------------------------|------------------------| | 组件私有状态 | `@State` | | 父→子单向数据流 | `@Prop` | | 父子双向数据同步 | `@Link` | | 跨层级共享数据 | `@Provide` / `@Consume`| | 监听数据变化执行逻辑 | `@Watch` | | 对象内部属性更新 | `@Observed` + `@ObjectLink` | 通过合理组合这些装饰器,可以高效实现 **数据驱动UI** 的响应式开发模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值