
前言
在 萌宠日记 中,页面间数据传递 是一个核心需求。当用户在 首页 点击"宠物档案"时,需要通知 Index 父组件 执行 NavPathStack.pushPath 跳转到子页面。这种 子 → 父 的通信方式,我们采用了 回调函数模式 --- 父组件通过属性传入 lambda 函数,子组件在合适的时机调用。
本文将从 萌宠日记 的回调函数实践出发,深入解析父子组件通信的几种模式,以及回调函数的设计原则和最佳实践。
一、回调函数模式概述
1.1 什么是回调函数模式
回调函数模式是 父组件将函数作为属性传递给子组件,子组件在需要时调用该函数,从而触发父组件中的逻辑。
父组件(Index) 子组件(HomePage)
│ │
│ 传递 lambda 回调函数 ──────→ │
│ (onNavigateToPetProfile) │
│ │
│ │ 用户点击"档案"
│ │
│ ←── 调用回调函数 ────────── │
│ (执行 pushPath 跳转) │
1.2 萌宠日记的回调实现
typescript
// Index.ets --- 父组件传递回调函数
HomePage({
onNavigateToPetProfile: () => {
this.homeStack.pushPath({ name: 'petProfile' })
},
onNavigateToTimeline: () => {
this.homeStack.pushPath({ name: 'timeline' })
},
onNavigateToCommunity: () => {
this.homeStack.pushPath({ name: 'community' })
}
})
提示 :回调函数是闭包(lambda),它可以捕获父组件作用域中的
this.homeStack,从而在子组件中触发父组件的导航逻辑。这是 ArkTS 中实现"子组件通知父组件"的标准模式。
二、回调函数的定义
2.1 属性声明
typescript
// HomePage.ets --- 定义可选的回调函数属性
export struct HomePage {
// 使用可选属性(?),表示可以不传
onNavigateToPetProfile?: () => void
onNavigateToTimeline?: () => void
onNavigateToCommunity?: () => void
}
2.2 回调签名设计
| 回调函数 | 签名 | 说明 |
|---|---|---|
onNavigateToPetProfile |
() => void |
导航到宠物档案页 |
onNavigateToTimeline |
() => void |
导航到成长时间轴 |
onNavigateToCommunity |
() => void |
导航到社区发现 |
2.3 可选 vs 必选
typescript
// 可选回调(推荐):使用 ? 标记,调用前判空
export struct HomePage {
onNavigateToPetProfile?: () => void // 可选
onClickProfile(): void {
if (this.onNavigateToPetProfile) { // 判空
this.onNavigateToPetProfile()
}
}
}
// 必选回调:不标记 ?,但必须传值
export struct HomePage {
onNavigateToPetProfile: () => void = () => {} // 默认空函数
}
三、回调函数的调用
3.1 调用时机
typescript
// HomePage.ets --- 在用户交互时调用回调
build() {
// ...
// 编辑按钮点击
Text('✏️')
.fontSize(20)
.fontColor('#F5A623')
.onClick(() => {
if (this.onNavigateToPetProfile) {
this.onNavigateToPetProfile() // 触发父组件导航
}
})
// ...
}
3.2 安全调用模式
typescript
// 安全调用模式 --- 调用前判空
if (this.onNavigateToPetProfile) {
this.onNavigateToPetProfile()
}
// 或者使用可选链调用(ES2020+)
this.onNavigateToPetProfile?.()
四、完整通信链路
4.1 从点击到跳转
用户点击首页的"编辑"按钮(✏️)
↓
HomePage.onClick 触发
↓
回调函数调用:this.onNavigateToPetProfile?.()
↓
Index 中传入的 lambda 执行
↓
this.homeStack.pushPath({ name: 'petProfile' })
↓
Navigation 渲染 NavDestination → PetProfilePage
4.2 代码链路
typescript
// 1. 父组件定义回调(Index.ets)
HomePage({
onNavigateToPetProfile: () => {
this.homeStack.pushPath({ name: 'petProfile' })
}
})
// 2. 子组件声明属性(HomePage.ets)
export struct HomePage {
onNavigateToPetProfile?: () => void
}
// 3. 子组件调用回调(HomePage.ets 中用户点击时)
Text('✏️')
.onClick(() => {
this.onNavigateToPetProfile?.()
})
五、多个回调的组织
5.1 批量传递
typescript
// Index.ets --- 一次性传递多个回调
HomePage({
onNavigateToPetProfile: () => {
this.homeStack.pushPath({ name: 'petProfile' })
},
onNavigateToTimeline: () => {
this.homeStack.pushPath({ name: 'timeline' })
},
onNavigateToCommunity: () => {
this.homeStack.pushPath({ name: 'community' })
}
})
5.2 回调分组
typescript
// 将回调分组到一个接口中
interface HomePageCallbacks {
onNavigateToPetProfile: () => void
onNavigateToTimeline: () => void
onNavigateToCommunity: () => void
}
// 使用接口定义属性
export struct HomePage {
callbacks?: HomePageCallbacks
}
// 调用时
this.callbacks?.onNavigateToPetProfile()
六、带参数的回调
6.1 传递数据
typescript
// 带参数的回调定义
export struct HomePage {
onNavigateToPetProfile?: (petId: string) => void
}
// 调用时传递参数
.onClick(() => {
this.onNavigateToPetProfile?.('123')
})
// 父组件接收参数
HomePage({
onNavigateToPetProfile: (petId: string) => {
this.homeStack.pushPath({
name: 'petProfile',
param: { petId: petId }
})
}
})
6.2 参数类型
| 回调参数类型 | 说明 | 示例 |
|---|---|---|
string |
字符串标识 | petId, diaryId |
number |
数字索引 | selectedIndex |
Object |
复杂对象 | { id: '123', name: '豆豆' } |
void |
无参数 | 仅触发动作 |
七、回调与 @Link 的对比
7.1 两种通信方式
| 对比维度 | 回调函数 | @Link 双向绑定 |
|---|---|---|
| 通信方向 | 子 → 父 | 父 → 子 / 双向 |
| 数据流 | 事件流 | 状态流 |
| 触发方式 | 主动调用 | 自动同步 |
| 适用场景 | 导航、事件通知 | 数据共享、状态同步 |
| 类型安全 | 强类型 | 强类型 |
7.2 选择建议
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 子组件通知父组件跳转 | 回调函数 | 事件驱动,语义清晰 |
| 子组件需要父组件数据 | @Link | 数据自动同步 |
| 子组件修改父组件数据 | @Link | 双向绑定 |
| 子组件触发父组件某个操作 | 回调函数 | 解耦,职责单一 |
八、回调函数的调试
8.1 调试技巧
typescript
// 在回调中添加日志
HomePage({
onNavigateToPetProfile: () => {
console.log('[Nav] Navigating to pet profile')
this.homeStack.pushPath({ name: 'petProfile' })
}
})
// 在子组件中追踪回调
if (this.onNavigateToPetProfile) {
console.log('[Callback] onNavigateToPetProfile is defined')
this.onNavigateToPetProfile()
} else {
console.warn('[Callback] onNavigateToPetProfile is undefined')
}
8.2 常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 点击无反应 | 回调未传递 | 检查父组件是否传入了回调 |
| 回调执行错误 | 回调中逻辑异常 | 检查回调函数中的代码 |
| 多次触发 | 重复绑定事件 | 检查是否多次绑定了 onClick |
| 回调未定义 | 属性声明为必选但未传值 | 使用可选属性 ? 或提供默认值 |
九、回调设计原则
9.1 命名规范
typescript
// 推荐的命名模式
onNavigateToPetProfile // on + 动作 + 目标
onSaveDiary // on + 动作 + 对象
onDeleteRecord // on + 动作 + 对象
onRefreshData // on + 动作 + 对象
onError // on + 事件
9.2 设计原则
有序列表 --- 回调函数设计的 5 个原则:
- 命名清晰 :以
on开头,描述触发的事件 - 参数简洁:参数数量尽量少(≤3 个),避免复杂对象
- 可选安全 :使用
?标记可选回调,调用前判空 - 单一职责:一个回调对应一个动作,不要合并多个逻辑
- 文档注释:使用 JSDoc 注释说明回调的触发时机和参数
十、扩展:EventHub 事件总线
10.1 跨组件事件
typescript
// 使用 EventHub 实现跨组件通信
// 发送事件
this.context.eventHub.emit('diarySaved', { diaryId: '123' })
// 接收事件
this.context.eventHub.on('diarySaved', (data: DiaryData) => {
console.log('Diary saved:', data.diaryId)
})
10.2 回调 vs EventHub 对比
| 对比维度 | 回调函数 | EventHub |
|---|---|---|
| 通信范围 | 父子组件 | 任意组件 |
| 耦合度 | 低(需显式传递) | 更低(事件解耦) |
| 类型安全 | 强类型 | 弱类型 |
| 调试难度 | 容易 | 较难追踪 |
| 适用场景 | 父子组件通信 | 跨层级、跨页面通信 |
总结
本文从 萌宠日记 的 回调函数模式 出发,深入解析了父子组件间数据传递的实现:
- 回调模式:父组件通过属性传入 lambda,子组件在适当时机调用
- 属性声明 :使用
?标记可选回调,确保空安全 - 调用时机:在用户交互事件中触发回调
- 安全调用 :
if (this.callback) { this.callback() }模式 - 带参回调:传递参数实现数据通信
- 与 @Link 对比:事件驱动 vs 状态驱动
- 设计原则:命名规范、参数简洁、单一职责
- EventHub:跨组件事件总线的扩展方案
回调函数模式是 ArkTS 中实现父子组件通信的最直接方式,也是萌宠日记导航架构中页面间通信的核心机制。
下一篇我们将深入 页面间数据传递 --- 参数化路由,解析通过 NavPathStack 传递路由参数的实现。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- ArkTS 组件通信:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-component-encapsulation
- @Link 装饰器:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-link
- @State 装饰器:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state
- EventHub 事件总线:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/eventhub
- 页面路由参数传递:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-routing
- 组件属性传递:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-attribute-animation-apis
- 状态管理概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management
- ArkTS 编程规范:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-coding-style-guide