鸿蒙进阶篇-状态管理之@Provide与@Consume
大家好,这里是鸿蒙开天组,今天我们来学习一下状态管理中的@Provide与@Consume。
一、概述
嘿!大家还记得这张图吗?不记得也要记得哦,因为这张图里的东西,既是高频必考面试题,也是实际开发中,经常需要用到的玩意,大致上可以这么理解:
下面Components框起来的部分,就是组件之间的状态变量的管理,也就是数据通讯,它会涉及到组件之间的关系,比如父子关系,比如祖孙关系或者其他跨级关系等等。
昨天我们已经学习了左下角的@Prop和@Link,今天需要学的就在中间位置:
@Provide/@Consume装饰的变量用于跨组件层级(多层组件)同步状态变量。
二、基础使用
先附上官方文档链接:@Provide装饰器和@Consume装饰器
官方文档写的东西比较多,对于新手来说不太容易理解消化和抓住重点,所以我们这里提供了基础结构:
// 写法 1:通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;
// 写法 2通过相同的变量别名绑定
@Provide b: number = 0;
@Consume('b') c: number;
简单来说,使用步骤就是:
- 将爷组件的状态属性使用@Provide修饰
- 孙组件通过@Consume修饰
三、例子说明
这里按步骤一步步来,先给出基础代码模板,大家理解一下其中的结构:
@Entry
@Component
// 爷级组件
struct GrandfatherComponent {
build() {
Column() {
Text('爷级组件')
.fontSize(30)
.fontWeight(900)
ParentComponent()
}
.padding(10)
.height('100%')
.backgroundColor(Color.Gray)
.width('100%')
.alignItems(HorizontalAlign.Center)
.padding({ top: 100 })
}
}
@Component
// 父级组件
struct ParentComponent {
// 编写 UI
build() {
Column({ space: 20 }) {
Text('我是父级组件')
.fontSize(22)
.fontWeight(900)
GrandsonComponent()
}
.backgroundColor(Color.Pink)
.alignItems(HorizontalAlign.Center)
.width('90%')
.margin({ top: 50 })
.padding(10)
.borderRadius(10)
}
}
@Component
// 孙级组件
struct GrandsonComponent {
// 编写 UI
build() {
Column({ space: 20 }) {
Text('我是孙级组件')
.fontSize(20)
.fontWeight(900)
}
.backgroundColor(Color.Orange)
.alignItems(HorizontalAlign.Center)
.width('90%')
.margin({ top: 50 })
.padding(10)
.borderRadius(10)
}
}
界面显示如下:
现在的需求是爷级组件和孙级组件通信传值,问题是,从代码结构上看,中间还隔了一个父级组件。虽然我们也可以基于上一篇讲的@State+@Prop,从爷->父->孙这样实现,但是管理起来毕竟有一个中间商不那么方便,而且这也是华为官方不推荐的做法,因为会消耗额外的性能。所以,我们采用了@Provide+@Consume的方式来实现:
interface Cat {
name: string
age: number
}
@Entry
@Component
// 爷级组件
struct GrandfatherComponent {
@Provide cat: Cat = { name: '小花', age: 3 }
@Provide food: string = 'fish'
build() {
Column() {
Text('爷级组件')
.fontSize(30)
.fontWeight(900)
ParentComponent()
}
.padding(10)
.height('100%')
.backgroundColor(Color.Gray)
.width('100%')
.alignItems(HorizontalAlign.Center)
.padding({ top: 100 })
}
}
@Component
// 父级组件
struct ParentComponent {
// 编写 UI
build() {
Column({ space: 20 }) {
Text('我是父级组件')
.fontSize(22)
.fontWeight(900)
GrandsonComponent()
}
.backgroundColor(Color.Pink)
.alignItems(HorizontalAlign.Center)
.width('90%')
.margin({ top: 50 })
.padding(10)
.borderRadius(10)
}
}
@Component
// 孙级组件
struct GrandsonComponent {
// 相同变量名
@Consume cat: Cat
@Consume('food') f: string
// 编写 UI
build() {
Column({ space: 20 }) {
Text('我是孙级组件')
.fontSize(20)
.fontWeight(900)
Text(JSON.stringify(this.cat))
Text(this.f)
Button('修改')
.onClick(() => {
this.cat.name = '小美'
this.cat.age = 99
this.f += '!'
})
}
.backgroundColor(Color.Orange)
.alignItems(HorizontalAlign.Center)
.width('90%')
.margin({ top: 50 })
.padding(10)
.borderRadius(10)
}
}
点击修改效果如下:
四、补充
1.@Provide+@Consume也是开发中很常用的一对,尤其是组件跨级关系复杂的时候,这个时候就只需要定义一个@Provide在祖级组件中定义,其他多个后代组件@Consume接收即可;
2.在build方法内,当@Provide与@Consume装饰的变量是Object类型、且通过a.b(this.object)形式调用时,b方法内传入的是this.object的原生对象,修改其属性,无法触发UI刷新,尝试思考下以下例子该如何修改,才能触发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);
})
}
}
}
好啦,今天的分享就到这里,感谢阅读,你的点赞和收藏都是莫大的支持!