DEV Community

Freerain
Freerain

Posted on

鸿蒙 Next 中 Provide 和 Consume 的用法总结

一、概述

在鸿蒙Next中,@Provide@Consume装饰器用于在祖先组件与后代组件之间实现双向数据同步,适用于状态数据在多个层级之间传递的场景,摆脱了父子组件间命名参数传递机制的束缚。从API version 9开始,这两个装饰器支持在ArkTS卡片中使用,从API version 11开始,支持在元服务中使用。

(一)功能特性

  1. @Provide装饰的状态变量自动对其所有后代组件可用,后代组件通过@Consume获取该变量,建立双向数据同步。
  2. 可以在多层级的父子组件之间传递数据,与@State/@Link不同,@Provide/@Consume不限于直接父子关系。
  3. 通过相同的变量名或变量别名进行绑定,建议类型相同,否则可能导致类型隐式转换和应用行为异常。

二、装饰器说明

(一)@Provide变量装饰器

  1. 参数
    • 别名(可选):常量字符串,若指定则通过别名绑定变量,未指定则通过变量名绑定。
  2. 同步类型:双向同步,与@State@Link组合的双向同步操作相同。
  3. 允许装饰的变量类型
    • Object、class、string、number、boolean、enum类型及其数组。
    • 支持Date类型。
    • API11及以上支持Map、Set类型。
    • 支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。
    • 必须指定类型,且@Provide变量与@Consume变量类型必须相同。不支持any,API11及以上支持联合类型(如string | number、string | undefined或ClassA | null)。
    • 当使用undefined和null时,建议显式指定类型。
  4. 被装饰变量的初始值:必须指定。
  5. 支持allowOverride参数(API11及以上):允许重写,声明后别名和属性名都可被Override,用于在同一组件树下处理同名@Provide变量。

(二)@Consume变量装饰器

  1. 参数
    • 别名(可选):若提供别名,则必须有@Provide的变量与其有相同别名才能匹配成功;否则需变量名相同才能匹配。
  2. 同步类型:双向同步,与@State@Link组合的双向同步操作相同。
  3. 允许装饰的变量类型
    • Object、class、string、number、boolean、enum类型及其数组。
    • 支持Date类型。
    • 支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。
    • 必须指定类型,且@Provide变量与@Consume变量类型必须相同。不支持any,API11及以上支持联合类型。
    • 当使用undefined和null时,建议显式指定类型。
  4. 被装饰变量的初始值:无,禁止本地初始化。
  5. 特殊要求:在其父组件或祖先组件上必须有对应的属性和别名的@Provide装饰的变量。

三、变量的传递/访问规则

(一)@Provide

  1. 从父组件初始化和更新:可选,允许多种父组件装饰变量初始化子组件@Provide,但常规变量变化不触发UI刷新,只有状态变量能触发。
  2. 用于初始化子组件:允许,可用于初始化@State@Link@Prop@Provide
  3. 和父组件同步:否。
  4. 和后代组件同步:和@Consume双向同步。
  5. 是否支持组件外访问:私有,仅在所属组件内访问。

(二)@Consume

  1. 从父组件初始化和更新:禁止,通过相同变量名和alias(别名)从@Provide初始化。
  2. 用于初始化子组件:允许,可用于初始化@State@Link@Prop@Provide
  3. 和祖先组件同步:和@Provide双向同步。
  4. 是否支持组件外访问:私有,仅在所属组件内访问。

四、观察变化和行为表现

(一)观察变化

  1. 基本类型(boolean、string、number):可观察到数值变化。
  2. class或Object类型:可观察到赋值和属性赋值变化(Object.keys返回的属性)。
  3. 数组类型:可观察到数组的添加、删除、更新单元操作。
  4. Date类型:可观察到整体赋值及通过接口更新属性操作。
  5. Map类型(API11及以上):可观察到整体赋值及通过接口更新值操作。
  6. Set类型(API11及以上):可观察到整体赋值及通过接口更新值操作。

(二)框架行为

  1. 初始渲染
    • @Provide装饰的变量以map形式传递给子组件。
    • 子组件使用@Consume变量时,在map中查找对应@Provide变量,找不到则抛出错误。
    • 初始化@Consume变量时,类似@State/@Link流程,在map中查找@Provide变量保存并注册。
  2. @Provide装饰的数据变化时
    • 父组件@Provide变量变更,遍历更新依赖它的系统组件和@Consume变量。
    • 通知@Consume更新后,子组件依赖@Consume的系统组件更新,实现@Provide@Consume状态数据同步。
  3. @Consume装饰的数据变化时
    • 子组件@Consume持有@Provide实例,更新后调用@Provide更新方法,将更新数值同步回@Provide

五、使用场景示例

(一)与后代组件双向同步状态

祖先组件CompA通过@Provide提供reviewVotes变量,后代组件CompD通过@Consume绑定该变量,实现双向同步。在CompACompD中点击按钮改变reviewVotes值,双方都会同步更新。

(二)装饰Map类型变量(API11及以上)

祖先组件MapSample通过@Provide提供messageMap<number, string>类型),后代组件Child通过@Consume绑定并操作该Map,如初始化、设置新值、清除、替换和删除元素等,视图会随之刷新,且操作同步到祖先组件。

(三)装饰Set类型变量(API11及以上)

祖先组件SetSample通过@Provide提供messageSet<number>类型),后代组件Child通过@Consume绑定并操作该Set,如初始化、添加元素、清除、删除元素等,视图会相应刷新,且操作同步到祖先组件。

(四)Provide_and_Consume支持联合类型实例

祖先组件Ancestors通过@Provide提供countstring | undefined类型),后代组件Child通过@Consume绑定。在祖先或后代组件中改变count属性或类型,另一方会对应刷新。

(五)@Provide支持allowOverride参数(API11及以上)

在同一组件树中,通过allowOverride参数可重写同名@Provide变量。例如GrandParent声明@Provide变量,ParentChild可通过设置allowOverride重写该变量,GrandSon通过@Consume绑定最近祖先的@Provide变量,若找不到则向上查找,否则报错。

六、常见问题及解决方法

(一)@BuilderParam尾随闭包情况下@Provide未定义错误

@BuilderParam尾随闭包场景中,要注意this的指向,避免出现找不到@Provide变量的错误。正确做法是在合适的祖先组件中定义@Provide变量,并确保在后代组件中能正确绑定和访问。

(二)使用a.b(this.object)形式调用,不会触发UI刷新

@Provide@Consume装饰的变量是Object类型,且在build方法内通过a.b(this.object)形式调用(如静态方法或组件内部方法修改Object属性)时,无法触发UI刷新。解决方法是先对变量进行赋值,使修改操作作用于带有Proxy代理的变量,从而实现UI刷新。

Top comments (0)