wap手机网站静态模板,免费公网网站建设,宣传页模板,公示区信息查询方法一、前言 Provide和Consume#xff0c;应用于与后代组件的双向数据同步#xff0c;应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递#xff0c;Provide和Consume摆脱参数传递机制的束缚#xff0c;实现跨层级传递。 其中Provi…一、前言 Provide和Consume应用于与后代组件的双向数据同步应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递Provide和Consume摆脱参数传递机制的束缚实现跨层级传递。 其中Provide装饰的变量是在祖先组件中可以理解为被“提供”给后代的状态变量。Consume装饰的变量是在后代组件中去“消费绑定”祖先组件提供的变量。 说明 从API version 9开始这两个装饰器支持在ArkTS卡片中使用。 从API version 11开始这两个装饰器支持在元服务中使用。 二、概述 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类型。支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。必须指定类型。Provide变量的Consume变量的类型必须相同。支持类型的场景请参考观察变化。不支持any。API11及以上支持上述支持类型的联合类型比如string | number, string | undefined 或者 ClassA | null示例见 Provide_and_Consume支持联合类型实例。注意当使用undefined和null的时候建议显式指定类型遵循TypeScript类型校验比如Provide a : string | undefined undefined是推荐的不推荐Provide a: string undefined。被装饰变量的初始值必须指定。支持allowOverride参数允许重写只要声明了allowOverride则别名和属性名都可以被Override。示例见Provide支持allowOverride参数。支持allowOverride参数 允许重写只要声明了allowOverride则别名和属性名都可以被Override。示例见Provide支持allowOverride参数。
Consume变量装饰器说明装饰器参数别名常量字符串可选。如果提供了别名则必须有Provide的变量和其有相同的别名才可以匹配成功否则则需要变量名相同才能匹配成功。同步类型双向从Provide变量具体请参见Provide到所有Consume变量以及相反的方向。双向同步操作与State和Link的组合相同。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。支持Date类型。支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。必须指定类型。Provide变量和Consume变量的类型必须相同。Consume装饰的变量在其父组件或者祖先组件上必须有对应的属性和别名的Provide装饰的变量。支持类型的场景请参考观察变化。不支持any。API11及以上支持上述支持类型的联合类型比如string | number, string | undefined 或者 ClassA | null示例见Provide_and_Consume支持联合类型实例。注意当使用undefined和null的时候建议显式指定类型遵循TypeScript类型校验比如Consume a : string |undefined。被装饰变量的初始值无禁止本地初始化。
四、变量的传递/访问规则说明
Provide传递/访问说明从父组件初始化和更新可选允许父组件中常规变量常规变量对Provide赋值只是数值的初始化常规变量的变化不会触发UI刷新只有状态变量才能触发UI刷新、State、Link、Prop、Provide、Consume、ObjectLink、StorageLink、StorageProp、LocalStorageLink和LocalStorageProp装饰的变量装饰变量初始化子组件Provide。用于初始化子组件允许可用于初始化State、Link、Prop、Provide。和父组件同步否。和后代组件同步和Consume双向同步。是否支持组件外访问私有仅可以在所属组件内访问。 Provide初始化规则图示 Consume传递/访问说明从父组件初始化和更新禁止。通过相同的变量名和alias别名从Provide初始化。用于初始化子组件允许可用于初始化State、Link、Prop、Provide。和祖先组件同步和Provide双向同步。是否支持组件外访问私有仅可以在所属组件内访问 Consume初始化规则图示 五、观察变化和行为表现
5.1 观察变化 当装饰的数据类型为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),selected: this.selectedDate})CompD()}}
}当装饰的变量是Map时可以观察到Map整体的赋值同时可通过调用Map的接口set, clear, delete 更新Map的值。详见装饰Map类型变量。当装饰的变量是Set时可以观察到Set整体的赋值同时可通过调用Set的接口add, clear, delete 更新Set的值。详见装饰Set类型变量。 六、框架行为
6.1 初始渲染 Provide装饰的变量会以map的形式传递给当前Provide所属组件的所有子组件子组件中如果使用Consume变量则会在map中查找是否有该变量名/alias别名对应的Provide的变量如果查找不到框架会抛出JS ERROR在初始化Consume变量时和State/Link的流程类似Consume变量会在map中查找到对应的Provide变量进行保存并把自己注册给Provide。 6.2 当Provide装饰的数据变化时 通过初始渲染的步骤可知子组件Consume已把自己注册给父组件。父组件Provide变量变更后会遍历更新所有依赖它的系统组件elementid和状态变量Consume通知Consume更新后子组件所有依赖Consume的系统组件elementId都会被通知更新。以此实现Provide对Consume状态数据同步。 6.3 当Consume装饰的数据变化时 通过初始渲染的步骤可知子组件Consume持有Provide的实例。在Consume更新后调用Provide的更新方法将更新的数值同步回Provide以此实现Consume向Provide的同步更新。 七、使用场景
在下面的示例是与后代组件双向同步状态Provide和Consume场景。当分别点击CompA和CompD组件内Button时reviewVotes 的更改会双向同步在CompA和CompD中。
Component
struct CompD {// Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的Provide装饰的变量Consume reviewVotes: number;build() {Column() {Text(reviewVotes(${this.reviewVotes}))Button(reviewVotes(${this.reviewVotes}), give 1).onClick(() this.reviewVotes 1)}.width(50%)}
}Component
struct CompC {build() {Row({ space: 5 }) {CompD()CompD()}}
}Component
struct CompB {build() {CompC()}
}Entry
Component
struct CompA {// Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件Provide reviewVotes: number 0;build() {Column() {Button(reviewVotes(${this.reviewVotes}), give 1).onClick(() this.reviewVotes 1)CompB()}}
}八、装饰Map类型变量 说明 从API version 11开始ProvideConsume支持Map类型。 在下面的示例中message类型为Mapnumber, string点击Button改变message的值视图会随之刷新。 Component
struct Child {Consume message: Mapnumber, stringbuild() {Column() {ForEach(Array.from(this.message.entries()), (item: [number, string]) {Text(${item[0]}).fontSize(30)Text(${item[1]}).fontSize(30)Divider()})Button(Consume init map).onClick(() {this.message new Map([[0, a], [1, b], [3, c]])})Button(Consume set new one).onClick(() {this.message.set(4, d)})Button(Consume clear).onClick(() {this.message.clear()})Button(Consume replace the first item).onClick(() {this.message.set(0, aa)})Button(Consume delete the first item).onClick(() {this.message.delete(0)})}}
}Entry
Component
struct MapSample {Provide message: Mapnumber, string new Map([[0, a], [1, b], [3, c]])build() {Row() {Column() {Button(Provide init map).onClick(() {this.message new Map([[0, a], [1, b], [3, c], [4, d]])})Child()}.width(100%)}.height(100%)}
}九、装饰Set类型变量 说明 从API version 11开始ProvideConsume支持Set类型。 在下面的示例中message类型为Set点击Button改变message的值视图会随之刷新。 Component
struct Child {Consume message: Setnumberbuild() {Column() {ForEach(Array.from(this.message.entries()), (item: [number, string]) {Text(${item[0]}).fontSize(30)Divider()})Button(Consume init set).onClick(() {this.message new Set([0, 1, 2, 3, 4])})Button(Consume set new one).onClick(() {this.message.add(5)})Button(Consume clear).onClick(() {this.message.clear()})Button(Consume delete the first one).onClick(() {this.message.delete(0)})}.width(100%)}
}Entry
Component
struct SetSample {Provide message: Setnumber new Set([0, 1, 2, 3, 4])build() {Row() {Column() {Button(Provide init set).onClick(() {this.message new Set([0, 1, 2, 3, 4, 5])})Child()}.width(100%)}.height(100%)}
}十、Provide_and_Consume支持联合类型实例 Provide和Consume支持联合类型和undefined和null在下面的示例中count类型为string | undefined点击父组件Parent中的Button改变count的属性或者类型Child中也会对应刷新。 Component
struct Child {// Consume装饰的变量通过相同的属性名绑定其祖先组件Ancestors内的Provide装饰的变量Consume count: string | undefined;build() {Column() {Text(count(${this.count}))Button(count(${this.count}), Child).onClick(() this.count Ancestors)}.width(50%)}
}Component
struct Parent {build() {Row({ space: 5 }) {Child()}}
}Entry
Component
struct Ancestors {// Provide装饰的联合类型count由入口组件Ancestors提供其后代组件Provide count: string | undefined Child;build() {Column() {Button(count(${this.count}), Child).onClick(() this.count undefined)Parent()}}
}十一、Provide支持allowOverride参数 allowOverrideProvide重写选项。 说明 从API version 11开始使用。 名称类型必填说明allowOverridestring否是否允许Provide重写。允许在同一组件树下通过allowOverride重写同名的Provide。如果开发者未写allowOverride定义同名的Provide运行时会报错。
Component
struct MyComponent {Provide({allowOverride : reviewVotes}) reviewVotes: number 10;
}Component
struct GrandSon {// Consume装饰的变量通过相同的属性名绑定其祖先内的Provide装饰的变量Consume(reviewVotes) reviewVotes: number;build() {Column() {Text(reviewVotes(${this.reviewVotes})) // Text显示10Button(reviewVotes(${this.reviewVotes}), give 1).onClick(() this.reviewVotes 1)}.width(50%)}
}Component
struct Child {Provide({ allowOverride: reviewVotes }) reviewVotes: number 10;build() {Row({ space: 5 }) {GrandSon()}}
}Component
struct Parent {Provide({ allowOverride: reviewVotes }) reviewVotes: number 20;build() {Child()}
}Entry
Component
struct GrandParent {Provide(reviewVotes) reviewVotes: number 40;build() {Column() {Button(reviewVotes(${this.reviewVotes}), give 1).onClick(() this.reviewVotes 1)Parent()}}
}在上面的示例中 GrandParent声明了Provide(“reviewVotes”) reviewVotes: number 40Parent是GrandParent的子组件声明Provide为allowOverride重写父组件GrandParent的Provide(“reviewVotes”) reviewVotes: number 40。如果不设置allowOverride则会抛出运行时报错提示Provide重复定义。Child同理。 GrandSon在初始化Consume的时候Consume装饰的变量通过相同的属性名绑定其最近的祖先的Provide装饰的变量。 GrandSon查找到相同属性名的Provide在祖先Child中所以Consume(“reviewVotes”) reviewVotes: number初始化数值为10。如果Child中没有定义与Consume同名的Provide则继续向上寻找Parent中的同名Provide值为20以此类推。 如果查找到根节点还没有找到key对应的Provide则会报初始化Consume找不到Provide的报错。 十二、常见问题
12.1 BuilderParam尾随闭包情况下Provide未定义错误 在此场景下CustomWidget执行this.builder()创建子组件CustomWidgetChild时this指向的是HomePage。因此找不到CustomWidget的Provide变量所以下面示例会报找不到Provide错误和BuilderParam连用的时候要谨慎this的指向。 错误示例
class Tmp {a: string
}Entry
Component
struct HomePage {Builderbuilder2($$: Tmp) {Text(${$$.a}测试)}build() {Column() {CustomWidget() {CustomWidgetChild({ builder: this.builder2 })}}}
}Component
struct CustomWidget {Provide(a) a: string abc;BuilderParambuilder: () void;build() {Column() {Button(你好).onClick(() {if (this.a ddd) {this.a abc;}else {this.a ddd;}})this.builder()}}
}Component
struct CustomWidgetChild {Consume(a) a: string;BuilderParambuilder: ($$: Tmp) void;build() {Column() {this.builder({ a: this.a })}}
}正确示例
class Tmp {name: string
}Entry
Component
struct HomePage {Provide(name) name: string abc;Builderbuilder2($$: Tmp) {Text(${$$.name}测试)}build() {Column() {Button(你好).onClick(() {if (this.name ddd) {this.name abc;} else {this.name ddd;}})CustomWidget() {CustomWidgetChild({ builder: this.builder2 })}}}
}Component
struct CustomWidget {BuilderParambuilder: () void;build() {this.builder()}
}Component
struct CustomWidgetChild {Consume(name) name: string;BuilderParambuilder: ($$: Tmp) void;build() {Column() {this.builder({ name: this.name })}}
}12.2 使用a.b(this.object)形式调用不会触发UI刷新 在build方法内当Provide与Consume装饰的变量是Object类型、且通过a.b(this.object)形式调用时b方法内传入的是this.object的原生对象修改其属性无法触发UI刷新。如下例中通过静态方法或者使用this调用组件内部方法修改组件中的this.dog.age与this.dog.name时UI不会刷新。 【反例】
class Animal {name:string;type:string;age: number;constructor(name:string, type:string, age:number) {this.name name;this.type type;this.age age;}static changeName1(animal:Animal) {animal.name Black;}static changeAge1(animal:Animal) {animal.age 1;}
}Entry
Component
struct Demo1 {Provide dog:Animal new Animal(WangCai, dog, 2);changeAge2(animal:Animal) {animal.age 2;}build() {Column({ space:10 }) {Text(Demo1: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.).fontColor(Color.Red).fontSize(30)Button(changeAge1).onClick((){// 通过静态方法调用无法触发UI刷新Animal.changeAge1(this.dog);})Button(changeAge2).onClick((){// 使用this通过自定义组件内部方法调用无法触发UI刷新this.changeAge2(this.dog);})Demo2()}}
}Component
struct Demo2 {build() {Column({ space:10 }) {Text(Demo2.).fontColor(Color.Blue).fontSize(30)Demo3()}}
}Component
struct Demo3 {Consume dog:Animal;changeName2(animal:Animal) {animal.name White;}build() {Column({ space:10 }) {Text(Demo3: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.).fontColor(Color.Yellow).fontSize(30)Button(changeName1).onClick((){// 通过静态方法调用无法触发UI刷新Animal.changeName1(this.dog);})Button(changeName2).onClick((){// 使用this通过自定义组件内部方法调用无法触发UI刷新this.changeName2(this.dog);})}}
}可以通过如下先赋值、再调用新赋值的变量的方式为this.dog加上Proxy代理实现UI刷新。 【正例】
class Animal {name:string;type:string;age: number;constructor(name:string, type:string, age:number) {this.name name;this.type type;this.age age;}static changeName1(animal:Animal) {animal.name Black;}static changeAge1(animal:Animal) {animal.age 1;}
}Entry
Component
struct Demo1 {Provide dog:Animal new Animal(WangCai, dog, 2);changeAge2(animal:Animal) {animal.age 2;}build() {Column({ space:10 }) {Text(Demo1: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.).fontColor(Color.Red).fontSize(30)Button(changeAge1).onClick((){// 通过赋值添加 Proxy 代理let a1 this.dog;Animal.changeAge1(a1);})Button(changeAge2).onClick((){// 通过赋值添加 Proxy 代理let a2 this.dog;this.changeAge2(a2);})Demo2()}}
}Component
struct Demo2 {build() {Column({ space:10 }) {Text(Demo2.).fontColor(Color.Blue).fontSize(30)Demo3()}}
}Component
struct Demo3 {Consume dog:Animal;changeName2(animal:Animal) {animal.name White;}build() {Column({ space:10 }) {Text(Demo3: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.).fontColor(Color.Yellow).fontSize(30)Button(changeName1).onClick((){// 通过赋值添加 Proxy 代理let b1 this.dog;Animal.changeName1(b1);})Button(changeName2).onClick((){// 通过赋值添加 Proxy 代理let b2 this.dog;this.changeName2(b2);})}}
}