HarmonyOS鸿蒙Next数学类上架项目解析31-组件通信与状态共享问题
HarmonyOS鸿蒙Next数学类上架项目解析31-组件通信与状态共享问题 在开发鸿蒙数学计算应用时,遇到组件通信问题:
- 父子组件之间数据传递不生效
- 兄弟组件之间无法直接通信
- 深层嵌套组件的状态传递繁琐
- 全局状态修改后部分组件未更新
如何在ArkTS应用中实现高效的组件通信和状态共享?
组件通信问题的核心是选择合适的状态管理方式。ArkTS提供了@State、@Prop、@Link、@Provide/@Consume等装饰器,需要根据场景正确使用。
1. 状态装饰器对比
/ @State: 组件内部状态,变化触发UI刷新 @Prop: 父传子,单向数据流,子组件可修改但不影响父组件 @Link: 父子双向绑定,子组件修改会同步到父组件 @Provide/@Consume: 跨层级状态共享,祖先提供,后代消费 @Observed/@ObjectLink: 嵌套对象的响应式更新 /
// 错误示例:@Prop修改不会同步到父组件
@Component
struct ChildWithProp {
[@Prop](/user/Prop) count: number // 单向传递
build() {
Button(`Count: ${this.count}`)
.onClick(() => {
this.count++ // 只修改本地副本,父组件不变
})
}
}
// 正确示例:使用@Link实现双向绑定
@Component
struct ChildWithLink {
[@Link](/user/Link) count: number // 双向绑定
build() {
Button(`Count: ${this.count}`)
.onClick(() => {
this.count++ // 父组件也会更新
})
}
}
2. 父子组件通信
/ 父传子:@Prop(单向)或 @Link(双向) 子传父:回调函数 /
// 父组件
@Entry
@Component
struct ParentComponent {
[@State](/user/State) parentCount: number = 0
[@State](/user/State) selectedItem: string = ''
build() {
Column() {
Text(`父组件计数: ${this.parentCount}`)
.fontSize(20)
.margin({ bottom: 20 })
// 使用[@Link](/user/Link)双向绑定
ChildCounter({ count: $parentCount })
// 使用回调函数接收子组件事件
ChildSelector({
items: ['选项A', '选项B', '选项C'],
onSelect: (item: string) => {
this.selectedItem = item
}
})
Text(`选中: ${this.selectedItem}`)
.margin({ top: 20 })
}
.width('100%')
.padding(20)
}
}
// 子组件:双向绑定
@Component
struct ChildCounter {
[@Link](/user/Link) count: number
build() {
Row() {
Button('-')
.onClick(() => { this.count-- })
Text(`${this.count}`)
.margin({ left: 20, right: 20 })
.fontSize(18)
Button('+')
.onClick(() => { this.count++ })
}
.margin({ top: 20 })
}
}
// 子组件:通过回调通知父组件
@Component
struct ChildSelector {
[@Prop](/user/Prop) items: string[]
onSelect: (item: string) => void = () => {}
build() {
Row() {
ForEach(this.items, (item: string) => {
Button(item)
.onClick(() => {
this.onSelect(item)
})
.margin({ right: 10 })
})
}
.margin({ top: 20 })
}
}
3. 跨层级组件通信
/ 使用@Provide/@Consume实现跨层级通信 祖先组件提供数据,任意后代组件可消费 /
// 定义共享状态类型
[@Observed](/user/Observed)
class ThemeState {
primaryColor: string = '#007AFF'
fontSize: number = 16
isDarkMode: boolean = false
}
// 祖先组件:提供状态
@Entry
@Component
struct AncestorComponent {
[@Provide](/user/Provide)('theme') theme: ThemeState = new ThemeState()
build() {
Column() {
Text('主题设置')
.fontSize(24)
.fontColor(this.theme.primaryColor)
// 主题控制面板
ThemeController()
// 深层嵌套的内容区域
ContentArea()
}
.width('100%')
.height('100%')
.backgroundColor(this.theme.isDarkMode ? '#1a1a1a' : '#ffffff')
}
}
// 中间层组件:不需要关心主题
@Component
struct ContentArea {
build() {
Column() {
Text('内容区域')
// 深层嵌套
DeepNestedComponent()
}
.padding(20)
}
}
// 深层嵌套组件:直接消费祖先提供的状态
@Component
struct DeepNestedComponent {
[@Consume](/user/Consume)('theme') theme: ThemeState
build() {
Column() {
Text('深层嵌套组件')
.fontSize(this.theme.fontSize)
.fontColor(this.theme.primaryColor)
Text('可以直接访问主题状态')
.fontSize(this.theme.fontSize - 2)
.fontColor(this.theme.isDarkMode ? '#cccccc' : '#666666')
}
.padding(16)
.backgroundColor(this.theme.isDarkMode ? '#333333' : '#f5f5f5')
.borderRadius(8)
}
}
// 主题控制组件:修改共享状态
@Component
struct ThemeController {
[@Consume](/user/Consume)('theme') theme: ThemeState
build() {
Column() {
Row() {
Text('深色模式')
Toggle({ type: ToggleType.Switch, isOn: this.theme.isDarkMode })
.onChange((isOn: boolean) => {
this.theme.isDarkMode = isOn
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 12 })
Row() {
Text('字体大小')
Slider({ value: this.theme.fontSize, min: 12, max: 24 })
.width(150)
.onChange((value: number) => {
this.theme.fontSize = value
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.padding(16)
.margin({ top: 20 })
}
}
4. 兄弟组件通信
/ 兄弟组件通信方案:
- 状态提升到共同父组件
- 使用事件总线
- 使用全局状态管理 /
// 方案1:状态提升
@Entry
@Component
struct ParentWithSiblings {
[@State](/user/State) sharedData: string = ''
build() {
Column() {
// 兄弟组件A:发送数据
SiblingA({
onDataChange: (data: string) => {
this.sharedData = data
}
})
// 兄弟组件B:接收数据
SiblingB({ data: this.sharedData })
}
}
}
@Component
struct SiblingA {
onDataChange: (data: string) => void = () => {}
build() {
Column() {
Text('组件A')
Button('发送数据')
.onClick(() => {
this.onDataChange('来自A的数据: ' + Date.now())
})
}
.padding(16)
.backgroundColor('#e3f2fd')
}
}
@Component
struct SiblingB {
[@Prop](/user/Prop) data: string
build() {
Column() {
Text('组件B')
Text(`接收到: ${this.data}`)
}
.padding(16)
.backgroundColor('#fff3e0')
}
}
// 方案2:事件总线
class EventBus {
private static listeners: Map<string, Array<(data: Object) => void>> = new Map()
static on(event: string, callback: (data: Object) => void): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, [])
}
this.listeners.get(event)?.push(callback)
}
static off(event: string, callback: (data: Object) => void): void {
const callbacks = this.listeners.get(event)
if (callbacks) {
const index = callbacks.indexOf(callback)
if (index > -1) callbacks.splice(index, 1)
}
}
static emit(event: string, data: Object): void {
this.listeners.get(event)?.forEach(cb => cb(data))
}
}
@Component
struct SiblingWithEventBus {
[@State](/user/State) receivedData: string = ''
private handler: ((data: Object) => void) | null = null
aboutToAppear(): void {
this.handler = (data: Object) => {
this.receivedData = (data as { message: string }).message
}
EventBus.on('sibling_message', this.handler)
}
aboutToDisappear(): void {
if (this.handler) {
EventBus.off('sibling_message', this.handler)
}
}
build() {
Column() {
Text(`收到: ${this.receivedData}`)
Button('发送给兄弟')
.onClick(() => {
EventBus.emit('sibling_message', { message: '你好兄弟!' })
})
}
}
}
5. 嵌套对象的响应式更新
/ 使用@Observed和@ObjectLink处理嵌套对象 /
[@Observed](/user/Observed)
class UserInfo {
name: string
age: number
address: Address
constructor(name: string, age: number, address: Address) {
this.name = name
this.age = age
this.address = address
}
}
[@Observed](/user/Observed)
class Address {
city: string
street: string
constructor(city: string, street: string) {
this.city = city
this.street = street
}
}
@Entry
@Component
struct NestedObjectPage {
[@State](/user/State) user: UserInfo = new UserInfo(
'张三',
25,
new Address('北京', '长安街')
)
build() {
Column() {
Text('用户信息')
.fontSize(24)
.margin({ bottom: 20 })
// 传递嵌套对象
UserInfoCard({ user: this.user })
// 传递深层嵌套对象
AddressCard({ address: this.user.address })
Button('修改城市')
.onClick(() => {
// 直接修改嵌套对象的属性
this.user.address.city = '上海'
})
.margin({ top: 20 })
}
.width('100%')
.padding(20)
}
}
@Component
struct UserInfoCard {
[@ObjectLink](/user/ObjectLink) user: UserInfo
build() {
Column() {
Text(`姓名: ${this.user.name}`)
Text(`年龄: ${this.user.age}`)
Button('增加年龄')
.onClick(() => {
this.user.age++ // 会触发UI更新
})
}
.padding(16)
.backgroundColor('#f5f5f5')
.borderRadius(8)
.margin({ bottom: 16 })
}
}
@Component
struct AddressCard {
[@ObjectLink](/user/ObjectLink) address: Address
build() {
Column() {
Text(`城市: ${this.address.city}`)
Text(`街道: ${this.address.street}`)
Button('修改街道')
.onClick(() => {
this.address.street = '南京路' // 会触发UI更新
})
}
.padding(16)
.backgroundColor('#e8f5e9')
.borderRadius(8)
}
}
6. 全局状态管理
/ 简单的全局状态管理 /
[@Observed](/user/Observed)
class GlobalStore {
private static instance: GlobalStore | null = null
// 全局状态
userToken: string = ''
isLoggedIn: boolean = false
cartItems: CartItem[] = []
private constructor() {}
static getInstance(): GlobalStore {
if (!GlobalStore.instance) {
GlobalStore.instance = new GlobalStore()
}
return GlobalStore.instance
}
// 操作方法
login(token: string): void {
this.userToken = token
this.isLoggedIn = true
}
logout(): void {
this.userToken = ''
this.isLoggedIn = false
this.cartItems = []
}
addToCart(item: CartItem): void {
this.cartItems = [...this.cartItems, item]
}
removeFromCart(index: number): void {
this.cartItems = this.cartItems.filter((_, i) => i !== index)
}
}
interface CartItem {
id: string
name: string
price: number
}
// 在应用入口提供全局状态
@Entry
@Component
struct AppEntry {
[@Provide](/user/Provide)('store') store: GlobalStore = GlobalStore.getInstance()
build() {
Column() {
// 应用内容
MainContent()
}
}
}
// 任意组件消费全局状态
@Component
struct CartButton {
[@Consume](/user/Consume)('store') store: GlobalStore
build() {
Button(`购物车 (${this.store.cartItems.length})`)
.onClick(() => {
// 导航到购物车页面
})
}
}
@Component
struct LoginStatus {
[@Consume](/user/Consume)('store') store: GlobalStore
build() {
if (this.store.isLoggedIn) {
Button('退出登录')
.onClick(() => {
this.store.logout()
})
} else {
Button('登录')
.onClick(() => {
this.store.login('mock_token')
})
}
}
}
总结
组件通信和状态共享的关键点:
- @Prop用于父传子单向数据流
- @Link用于父子双向绑定
- @Provide/@Consume用于跨层级状态共享
- @Observed/@ObjectLink用于嵌套对象响应式
- 兄弟组件通信可通过状态提升或事件总线
- 复杂应用使用全局状态管理
- 选择合适的方案避免过度设计
- 项目链接:https://gitee.com/solgull/math-fbox
更多关于HarmonyOS鸿蒙Next数学类上架项目解析31-组件通信与状态共享问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


