鸿蒙HarmonyOS开发:跨组件层级数据双向同步指导
创始人
2024-09-26 04:47:12
0

@Provider和@Consumer用于跨组件层级数据双向同步,可以使得开发者不拘泥于组件层级。

@Provider和@Consumer属于状态管理V2装饰器,所以只能在@ComponentV2中才能使用,在@Component中使用会编译报错。

@Provider和@Consumer装饰器从API version 12开始支持。

当前状态管理(V2试用版)仍在逐步开发中,相关功能尚未成熟,建议开发者尝鲜试用。

概述

@Provider,即数据提供方,其所有的子组件都可以通过@Consumer绑定相同的key来获取@Provider提供的数据。

@Consumer,即数据消费方,可以通过绑定同样的key获取其最近父节点的@Provider的数据,当查找不到@Provider的数据时,使用本地默认值。

@Provider和@Consumer装饰数据类型需要一致。

开发者在使用@Provider和@Consumer时要注意:

  • @Provider和@Consumer强依赖自定义组件层级,@Consumer所在组件会由于其父组件的不同,而被初始化为不同的值。
  • @Provider和@Consumer相当于把组件粘合在一起了,从组件独立角度,要减少使用@Provider和@Consumer。

@Provider和@Consumer vs @Provide和@Consume能力对比

在状态管理V1版本中,提供跨组件层级双向的装饰器为@Provide和@Consume,当前文档介绍的是状态管理V2装饰器@Provider和@Consumer。虽然两者名字和功能类似,但在特性上还存在一些差异。

如果开发者对状态管理V1中@Provide和@Consume完全不曾了解过,可以直接跳过本节。

能力V2装饰器@Provider和@ConsumerV1装饰器@Provide和@Consume
@Consume(r)允许本地初始化,当找不到@Provider的时候使用本地默认值。禁止本地初始化,当找不到对应的的@Provide时候,会抛出异常。
支持类型支持function。不支持function。
观察能力仅能观察自身赋值变化,如果要观察嵌套场景,配合@Trace一起使用。观察第一层变化,如果要观察嵌套场景,配合@Observed和@ObjectLink一起使用。
alias和属性名alias是唯一匹配的key,如果缺省alias,则默认属性名为alias。alias和属性名都为key,优先匹配alias,匹配不到可以匹配属性名。
@Provide(r) 从父组件初始化禁止。允许。
@Provide(r)支持重载默认开启,即@Provider可以重名,@Consumer向上查找最近的@Provider。默认关闭,即在组件树上不允许有同名@Provide。如果需要重载,则需要配置allowOverride。

装饰器说明

基本规则

@Provider语法:

@Provider(alias?: string) varName : varType = initValue

@Provider属性装饰器说明
装饰器参数aliasName?: string,别名,缺省时默认为属性名。
支持类型自定义组件中成员变量。属性的类型可以为number、string、boolean、class、Array、Date、Map、Set等类型。支持修饰箭头函数。
从父组件初始化禁止。
本地初始化必须本地初始化。
观察能力能力等同于@Trace。变化会同步给对应的@Consumer。

@Consumer语法:

@Consumer(alias?: string) varName : varType = initValue

@Consumer属性装饰器说明
装饰器参数aliasName?: string,别名,缺省时默认为属性名,向上查找最近的@Provider。
可装饰的变量自定义组件中成员变量。属性的类型可以为number、string、boolean、class、Array、Date、Map、Set等类型。支持修饰箭头函数。
从父组件初始化禁止。
本地初始化必须本地初始化。
观察能力能力等同于@Trace。变化会同步给对应的@Provider。

aliasName和属性名

@Provider和@Consumer可接受可选参数aliasName,如果开发者没有配置参数,则使用属性名作为默认值。注意:aliasName是@Provider和@Consumer匹配唯一指定key。

以下三个例子可清楚介绍@Provider和@Consumer在使用aliasName查找关系。

@ComponentV2 struct Parent {     @Provider() str: string = 'hello';   // no aliasName, use propertyName "str" as aliasName }  @ComponentV2 struct Child {     @Consumer('str') str: string = 'world';   // use aliasName 'str' to find                                               // can find in Parent, use Provider value 'hello' }
@ComponentV2 struct Parent {     @Provider('alias') str: string = 'hello';   // has alias }  @ComponentV2 struct Child {     @Consumer('alias') str: string = 'world';   // use aliasName 'alias' to find Provider value 'hello' }
@ComponentV2 struct Parent {     @Provider('alias') str: string = 'hello';   // has alias }  @ComponentV2 struct Child {     @Consumer() str: string = 'world';   // no aliasName, use propertyName "str" as aliasName, cannot find Provider, so use the local value 'world' }

使用限制

  1. @Provider和@Consumer为自定义组件的属性装饰器,仅能修饰自定义组件内的属性,不能修饰class的属性。
  2. @Provider和@Consumer为新状态管理装饰器,只能在@ComponentV2中使用,不能在@Component中使用。

使用场景

@Provider和@Consumer双向同步

建立双向绑定

  1. 自定义组件Parent和Child初始化:
    • Child中@Consumer() str: string = 'world'向上查找,查找到Parent中声明的@Provider() str: string = 'hello'。
    • @Consumer() str: string = 'world'初始化为其查找到的@Provider的值,为‘hello’。
    • 两者建立双向同步关系。
  2. 点击Parent中的Button,改变@Provider装饰的str,通知其对应的@Consumer。对应UI刷新。
  3. 点击Child中Button,改变@Consumer装饰的str,通知其对应的@Provider。对应UI刷新。
@Entry @ComponentV2 struct Parent {   @Provider() str: string = 'hello';    build() {     Column() {       Button(this.str)         .onClick(() => {           this.str += '0';         })       Child()     }   } }    @ComponentV2 struct Child {   @Consumer() str: string = 'world';    build() {     Column() {       Button(this.str)         .onClick(() => {           this.str += '0';         })     }   } }

未双向绑定

下面的例子中,@Provider和@Consumer由于key值不同,无法建立双向同步关系。

  1. 自定义组件Parent和Child初始化:
    • Child中@Consumer() str: string = 'world'向上查找,未查找到其数据提供方@Provider。
    • @Consumer() str: string = 'world'使用其本地默认值为‘world’。
    • 两者未建立双向同步关系。
  2. 点击Parent中的Button,改变@Provider装饰的str1,刷新@Provider关联的Button组件。
  3. 点击Child中Button,改变@Consumer装饰的str,刷新@Consumer关联的Button组件。
@Entry @ComponentV2 struct Parent {   @Provider() str1: string = 'hello';    build() {     Column() {       Button(this.str1)         .onClick(() => {           this.str1 += '0';         })       Child()     }   } }    @ComponentV2 struct Child {   @Consumer() str: string = 'world';    build() {     Column() {       Button(this.str)         .onClick(() => {           this.str += '0';         })     }   } }

@Provider和@Consumer可以修饰回调事件,方便组件之间完成行为抽象

当需要在父组件中需要给子组件注册回调函数,可以通过@Provider和@Consumer修饰回调方法来解决。

比如拖拽场景,当发生拖拽事件时,如果希望将子组件的拖拽的起始位置信息同步给父组件。如下面的例子。

@Entry @ComponentV2 struct Parent {   @Local childX: number = 0;   @Local childY: number = 1;   @Provider() onDrag: (x: number, y: number) => void = (x: number, y: number) => {     console.log(`onDrag event at x=${x} y:${y}`);     this.childX = x;     this.childY = y;   }    build() {     Column() {       Text(`child postion x: ${this.childX}, y: ${this.childY}`)       Child()     }   } }  @ComponentV2 struct Child {   @Consumer() onDrag: (x: number, y: number) => void = (x: number, y: number) => {};    build() {     Button("changed")       .draggable(true)       .onDragStart((event: DragEvent) => {         // 当前预览器上不支持通用拖拽事件         this.onDrag(event.getDisplayX(), event.getDisplayY());       })   } }

@Provider和@Consumer修饰复杂类型,配合@Trace一起使用

  1. @Provider和@Consumer只能观察到数据本身的变化。如果当其修饰复杂数据类型,需要观察属性的变化,需要配合@Trace一起使用。
  2. 修饰buildin type:Array、Map、Set、Data时,可以观察到某些API的变化,观察能力同@Trace。
@ObservedV2 class User {   @Trace name: string;   @Trace age: number;    constructor(name: string, age: number) {     this.name = name;     this.age = age;   } }  const data: User[] = [new User('Json', 10), new User('Eric', 15)];  @Entry @ComponentV2 struct Parent {   @Provider('data') users: User[] = data;    build() {     Column() {       Child()       Button('age new user')         .onClick(() => {           this.users.push(new User('Molly', 18));         })       Button('age++')         .onClick(() => {           this.users[0].age++;         })       Button('change name')         .onClick(() => {           this.users[0].name = 'Shelly';         })     }   } }    @ComponentV2 struct Child {   @Consumer('data') users: User[] = [];    build() {     Column() {       ForEach(this.users, (item: User) => {         Column() {           Text(`name: ${item.name}`).fontSize(30)           Text(`age: ${item.age}`).fontSize(30)           Divider()         }       })     }   } }

@Provider重名,@Consumer向上查找其最近的@Provider

@Provider可以在组件树上重名,@Consumer会向上查找其最近父节点的@Provider的数据。

  • AComp中@Consumer向上查找,查找到Parent中定义的 @Provider() val: number = 10,所以初始化为10。
  • A1Comp中@Consumer向上查找,查找到AComp中定义的@Provider() val: number = 20,即停止,不会继续向上查找,所以初始化为20。
@Entry @ComponentV2 struct Parent {   @Provider() val: number = 10;    build() {     Column() {       AComp()     }    } }  @ComponentV2 struct AComp {   @Provider() val: number = 20;   @Consumer("val") val2: number = 0; // 10    build() {     Column() {       Text(`${this.val2}`)       A1Comp()     }    } }  @ComponentV2 struct A1Comp {   @Consumer() val: number = 0; // 20    build() {     Text(`${this.val}`)   } }

@Provider和@Consumer初始化@Param

  • 点击Text(@Consumer val: ${this.val}),触发@Consumer() val的变化,变化同步给Parent中@Provider() val,从而触发子组件Text(@Param val2: ${this.val2})的变化。
  • @Consumer() val的变化也会同步给A1Comp,触发Text(A1Comp @Param val ${this.val})的改变。
@Entry @ComponentV2 struct Parent {   @Provider() val: number = 10;    build() {     Column() {       AComp({ val2: this.val })     }   } }  @ComponentV2 struct AComp {   @Consumer() val: number = 0;   @Param val2: number = 0;    build() {     Column() {       Text(`AComp @Consumer val: ${this.val}`).fontSize(30).onClick(() => {         this.val++;       })       Text(`AComp @Param val2: ${this.val2}`).fontSize(30)       A1Comp({ val: this.val })     }.border({ width: 2, color: Color.Green })   } }  @ComponentV2 struct A1Comp {   @Param val: number = 0;    build() {     Column() {       Text(`A1Comp @Param val ${this.val}`).fontSize(30)     }.border({ width: 2, color: Color.Pink })   } }

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

点击领取→【纯血版鸿蒙全套最新学习资料】(安全链接,放心点击希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取~限时开源!!


 鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。


HarmonyOS Next 最新全套视频教程

 《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

大厂面试必问面试题

鸿蒙南向开发技术

鸿蒙APP开发必备


请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,才能在这个变革的时代中立于不败之地。 

相关内容

热门资讯

安卓系统为什么不封闭,揭秘安卓... 你有没有想过,为什么安卓系统那么开放,却不像苹果iOS那样封闭呢?这背后可是有着不少有趣的故事和原因...
安卓系统更新包多大,解析不同版... 你有没有发现,每次安卓系统更新,手机里都会多出那么几个G的文件?这可真是让人好奇,安卓系统更新包究竟...
安卓手机安装双系统吗,安卓手机... 你有没有想过,你的安卓手机是不是也能像电脑一样,装上两个系统,一个用来工作,一个用来娱乐?没错,这就...
oppo会升级安卓系统,畅享最... 你知道吗?最近有个大消息在手机圈里炸开了锅,那就是OPPO要升级安卓系统啦!这可不是什么小打小闹的更...
安卓系统上安装windows,... 你有没有想过,在安卓手机上安装Windows系统?听起来是不是有点不可思议?但你知道吗,这竟然是可能...
安卓系统怎么进运行框,安卓系统... 你有没有想过,你的安卓手机里有一个超级实用的功能,那就是运行框!它就像是一个小助手,帮你快速找到正在...
安卓系统电视无图像设置,安卓电... 你家的安卓系统电视突然没图像了?别急,让我来给你支个招,让你轻松解决这个问题!一、检查电源和连接线首...
安卓机建议升级系统吗,提升性能 你有没有发现,你的安卓手机最近有点儿慢吞吞的?是不是在犹豫要不要升级系统呢?别急,让我来给你好好分析...
升级不了安卓系统升级,探寻升级... 你有没有遇到过这种情况?手机里的安卓系统突然告诉你,它需要升级,但你左等右等,就是升不上去。这可真是...
备用安卓系统手机推荐,盘点热门... 你有没有想过,如果你的手机突然罢工了,你会怎么办?别担心,今天我就要给你安利几款备用安卓系统手机,让...
旧安卓系统ipad无法更新系统... 你有没有遇到过这种情况?你的旧安卓系统iPad突然告诉你,它无法更新系统了!是不是瞬间感觉心里有点小...
运行安卓6系统命令大全,全面掌... 你有没有想过,你的安卓手机里隐藏着无数强大的功能,只等着你去发现和探索呢?今天,就让我带你走进安卓6...
安卓系统简笔画教程下载,轻松绘... 你有没有想过,用简单的线条就能把复杂的安卓系统画出来?没错,就是那种一看就懂,一画就上手的感觉!今天...
华为如何鸿蒙转安卓系统,轻松实... 你知道吗?最近华为的大动作可是让整个科技圈都沸腾了!他们竟然把鸿蒙系统转到了安卓系统上,这可真是让人...
安卓10系统的问题,安卓10系... 你有没有发现,自从你的手机升级到安卓10系统后,好像有点不对劲呢?别急,让我来给你细细道来,看看安卓...
安卓系统苹果搞笑视频,苹果搞笑... 你知道吗?在互联网的世界里,搞笑视频可是个永恒的热门话题。尤其是那些结合了安卓系统和苹果手机的搞笑片...
卡片机改造安卓系统,探索改造之... 你有没有想过,那些曾经陪伴我们记录美好时光的卡片机,现在竟然也能焕发第二春呢?没错,就是那些小巧便携...
装安卓系统倒车出不来,智能科技... 你有没有遇到过这样的事情:手机装了个安卓系统,结果倒车的时候出不来啦?这可不是闹着玩的,简直让人抓狂...
定制安卓系统哪家好点儿,哪家服... 你有没有想过,手机系统就像是个人的衣服,每个人都需要找到最适合自己的那一款?今天,咱们就来聊聊定制安...
台电用回安卓系统吗,开启智能新... 最近有个话题在科技圈里炒得挺热的,那就是台电是不是要用回安卓系统了?你有没有想过,这个小小的决定背后...