前言
ETS(基于 TS 扩展的声明式开发范式)提供了强大的组件化能力,支持开发者高效的构建应用的 UI 界面,然而每一个组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互进行直接的引用,因此组件间的相互通信是非常重要的。
组件间通信原理
ETS 组件间通信是通过组合不同的装饰器来实现数据在各个组件之间的单向/双向传递。
相关装饰器介绍
组件间通信实现
父子组件通信
数据单向传递
使用 @State 和 @Prop 装饰器可以实现父组件向子组件单向传递数据。
●定义子组件 Child
@Component
struct Child {
@Watch("listenChildCount") //当count变量值发生变化,会调用listenChildCount()
@Prop
count: number;
build() {
Column() {
Text("子组件:" + this.count).fontSize(30)
Button("reduce")
.fontSize(25)
.onClick(() => {
this.count--;
})
}
.border({ width: 2, color: Color.Blue })
.backgroundColor(Color.Green)
.width("50%")
.height("50%")
}
/**
* 用于监听count变量的变化
*/
listenChildCount() {
console.info("ChildComponent count changed:" + this.count)
}
}
●定义父组件,在父组件中嵌套子组件 Child,并将父组件中的 count 变量传递给子组件
@Entry
@Component
struct Parent {
@State
@Watch("listenCount") //当count变量值发生变化,会调用listenCount()
count: number = 0;
build() {
Column() {
Column() {
Text("父组件:" + this.count)
.fontSize(30)
.fontColor(Color.Red)
Button("add")
.fontColor(Color.White)
.fontSize(30)
.backgroundColor(Color.Orange)
.onClick(() => {
this.count++;
})
Child({ count: this.count })
}.border({ width: 2, color: Color.Black })
.width("100%")
.height(400)
.backgroundColor(Color.Gray)
}
}
listenCount() {
console.info("ParentComponent count changed:" + this.count)
}
}
效果如下:
数据双向传递
@State/@Link 实现父子组件数据双向传递
●将上文的子组件中 count 变量上的装饰器改为 @Link
@Component
struct Child {
@Link
@Watch("listenCount")
count: number;
build() {
...
...
}
...
}
●改变上文中父组件传递数据到子组件的方式
@Entry
@Component
struct Parent {
...
build() {
Column() {
...
...
Child({ count: $count }) //@Link需要用'$'符号传递变量
}.border({ width: 2, color: Color.Black })
.width("100%")
.height(400)
.backgroundColor(Color.Gray)
}
...
}
效果如下:
@Provide/@Consume 实现父子组件数据双向传递
将上文父组件中 count 变量上的装饰器替换为 @Provide,子组件中 count 变量上的装饰器替换成 @Consume:
@Entry
@Component
struct Parent {
//这里@Provide必须放在@Watch的上面,否则会报错
@Provide("Count")
@Watch("listenCount") //当count变量值发生变化,会调用listenCount()
count: number = 0;
build() {
Column() {
Column() {
Text("父组件:" + this.count)
.fontSize(30)
.fontColor(Color.Red)
Button("add")
.fontColor(Color.White)
.fontSize(30)
.backgroundColor(Color.Orange)
.onClick(() => {
this.count++;
})
Child() //使用@Provide 、@Consume不需要像@Link、@Prop那样传数据
}.border({ width: 2, color: Color.Black })
.width("100%")
.height(400)
.backgroundColor(Color.Gray)
}
}
listenCount() {
console.info("ParentComponent count changed:" + this.count)
}
}
@Component
struct Child {
//这里@Consume必须放在@Watch的上面,否则会报错
@Consume("Count") //@Consume的参数值必须和对应的@Provide的参数值一致
@Watch("listenChildCount") //当count变量值发生变化,会调用listenChildCount()
count: number;
build() {
Column() {
Text("子组件:" + this.count)
.fontSize(30)
Button("reduce")
.fontSize(25)
.onClick(() => {
this.count--;
})
}
.border({ width: 2, color: Color.Blue })
.backgroundColor(Color.Green)
.width("50%")
.height("50%")
}
listenChildCount() {
console.info("ChildComponent count changed:" + this.count)
}
}
效果:效果与上一步相同
@Provide/@Consume 实现组件跨层级双向传递数据
●定义子组件 GrandChild
@Component
struct GrandChild {
@Consume
count: number;
build() {
Column() {
Text("GrandChild:" + this.count)
.fontSize(30)
Button("reduce")
.fontSize(25)
.onClick(() => {
this.count--;
})
}
.border({ width: 2, color: Color.Blue })
.backgroundColor(Color.Gray)
.width(200)
.height(180)
}
}
●定义子组件 Child,在 Child 组件中嵌套 GrandChild 组件
@Component
struct Child {
build() {
Column() {
Text("Child")
.fontSize(40)
.margin({ top: 25 })
GrandChild()
}
.border({ width: 2, color: Color.Blue })
.backgroundColor(Color.Green)
.width(300)
.height(300)
}
}
●定义父组件 Parent,在 Parent 组件中嵌套 Child 组件
@Entry
@Component
struct Parent {
@Provide("Count")
count: number = 0;
build() {
Column() {
Column() {
Text("Parent:" + this.count)
.fontSize(30)
.fontColor(Color.Red)
Button("add")
.fontColor(Color.White)
.fontSize(30)
.backgroundColor(Color.Orange)
.onClick(() => {
this.count++;
})
Child()
}.border({ width: 2, color: Color.Black })
.width("100%")
.height(400)
.backgroundColor(Color.Gray)
}
}
}
效果如下:
兄弟组件通信
ETS 中要实现兄弟组件间传递数据需要借助第三个组件,即两个需要传递数据的组件需要有共同的父组件
@Provide/@Consume 实现兄弟组件双向传递数据
●定义子组件 Child1、Child2,并用 @Consume 装饰对应变量
@Component
struct Child1 {
@Consume("Count")
count1: number
build() {
Column() {
Text("Child1:" + this.count1)
.fontSize(40)
Button("add")
.fontColor(Color.White)
.fontSize(30)
.backgroundColor(Color.Orange)
.onClick(() => {
this.count1++;
})
}
.border({ width: 2, color: Color.Blue })
.backgroundColor(Color.Green)
.width("100%")
.height(180)
}
}
@Component
struct Child2 {
@Consume("Count")
count2: number;
build() {
Column() {
Text("Child2:" + this.count2)
.fontSize(30)
Button("reduce")
.fontSize(25)
.onClick(() => {
this.count2--;
})
}
.border({ width: 2, color: Color.Blue })
.backgroundColor(Color.Yellow)
.width("100%")
.height(180)
}
}
●定义父组件 Parent,嵌套组件 Child1、Child2
@Entry
@Component
struct Parent {
@Provide("Count")
count: number = 0;
build() {
Column() {
Child1()
Child2()
}
.width("100%")
.height("100%")
}
}
效果如下:
@State 和 @Link 实现兄弟组件双向传递数据
●将子组件 Child1 和 Child2 中 count 变量上的装饰器改为 @Link
@Component
struct Child1 {
@Link
count1: number
build() {
...
..
}
}
@Component
struct Child2 {
@Link
count2: number;
build() {
...
...
}
}
●将上一步父组件 Parent 中 count 变量上的装饰器改为 @State
@Entry
@Component
struct Parent {
@State
count: number = 0;
build() {
...
Child1({ count1: $count })
Child2({ count2: $count })
...
}
}
效果:与上一步的效果相同
任意组件通信
借助 AppStorage 和 @StorageLink 可以实现任意两个组件(包括同一个页面里的两个组件、不同页面里的组件、不同 Ability 里的两个组件)
AppStorage/@StorageLink 实现不同页面组件间传递数据
●定义页面 Page1
import router from '@ohos.router'; //router用于进行页面跳转
AppStorage.SetOrCreate("Count", 0); //先向AppStorage中存一个key:value
@Entry
@Component
struct Component1 {
@StorageLink("Count")
count: number = AppStorage.Link("Count") //与AppStorage进行双向绑定,用AppStorage里的值初始化count
build() {
Column() {
Text("页面1:" + this.count)
.fontSize(50)
Button("add")
.margin({ bottom: 25 })
.onClick(() => {
this.count++;
})
Button("跳转")
.fontSize(25)
.onClick(() => {
router.push({ url: "pages/page2" })
})
}.border({ width: 2, color: Color.Blue })
.width("100%")
.height(400)
.backgroundColor(Color.Gray)
}
}
●定义页面 Page2
import router from '@ohos.router';
@Entry
@Component
struct Component2 {
@StorageLink("Count")
count: number = AppStorage.Link("Count") //与AppStorage进行双向绑定,用AppStorage里的值初始化count
build() {
Column() {
Text("页面2:" + this.count)
.fontSize(50)
.fontColor(Color.Green)
Button("reduce")
.margin({ bottom: 25 })
.onClick(() => {
this.count--;
})
Button("跳转")
.fontSize(25)
.onClick(() => {
router.back({ url: "pages/page1" })
})
}.border({ width: 2, color: Color.Blue })
.width("100%")
.height(400)
.backgroundColor(Color.Orange)
}
}
效果如下:
写在最后
●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我两个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing ,不定期分享原创知识。