HarmonyOS6 PC 端组件通信实战:用聊天界面理解 @Prop 与事件回调
前言组件通信是 ArkUI 开发中绕不开的核心话题。当一个页面被拆分成多个组件时组件之间需要说话——父组件把数据传给子组件展示子组件把用户操作结果汇报给父组件处理。这套数据流转的机制就是组件通信。理解组件通信最好的方式就是用一个真实的场景来演示。本文选择了聊天气泡界面作为案例MessageBubble是子组件负责渲染单条消息气泡Demo598是父组件负责管理整个消息列表和输入框。通过这个案例你能直观感受到Prop单向传值和事件回调这两种通信方式的实际效果。整体设计思路在开始看代码之前先理清楚这个案例的组件结构Demo 父组件 ├── 状态messages 消息列表、inputText 输入内容、unreadCount 未读数 ├── MessageBubble × N子组件每条消息一个 │ ├── Prop sender发送者名称从父组件传入 │ ├── Prop content消息内容从父组件传入 │ └── Prop isMe是否是自己发的决定气泡位置和颜色 └── 输入区域TextInput 发送按钮 └── 点击发送时把新消息追加到父组件的 messages 数组数据流向是这样的父组件messages数组 → 遍历每条消息 → 通过Prop传给MessageBubble→ 渲染气泡。用户输入并发送 → 父组件messages更新 → 重新遍历渲染。MessageBubble聊天气泡子组件Componentstruct MessageBubble{Propsender:stringPropcontent:stringPropisMe:booleanfalsebuild(){Column(){Text(this.sender).fontSize(11).fontColor(#888888).margin({bottom:4})Text(this.content).fontSize(14).fontColor(this.isMe?#FFFFFF:#333333).padding(10).backgroundColor(this.isMe?#4D96FF:#F0F0F0).borderRadius(12).constraintSize({maxWidth:80%})}.alignItems(this.isMe?HorizontalAlign.End:HorizontalAlign.Start).width(100%).padding({left:this.isMe?40:8,right:this.isMe?8:40}).margin({bottom:8})}}这个组件用isMe这一个布尔值来区分我发的和对方发的两种气泡样式涉及四处条件判断文字颜色我发的是白色#FFFFFF对方发的是深色#333333气泡背景色我发的是蓝色#4D96FF对方发的是浅灰#F0F0F0对齐方向我发的靠右HorizontalAlign.End对方靠左HorizontalAlign.Start内边距我发的左边留 40dp 空白把气泡推到右边对方右边留 40dp把气泡推到左边.constraintSize({ maxWidth: 80% })限制气泡最大宽度为 80%防止很长的消息把气泡撑满整行这是聊天界面的标准设计。父组件状态定义与消息数据结构interfaceMessageItem{sender:stringcontent:stringisMe:boolean}EntryComponentstruct Demo598{StateisShow:booleantrueStatemessages:MessageItem[][{sender:客服,content:您好请问有什么可以帮您?,isMe:false},{sender:我,content:我想了解下 ArkUI 组件通信,isMe:true},{sender:客服,content:好的组件通信主要通过 Prop、Link、事件回调等方式实现,isMe:false},]StateinputText:stringStateunreadCount:number1...}注意这里先定义了interface MessageItem明确了每条消息的数据结构ArkTS 是强类型语言TypeScript 规范要求这样做。初始消息列表里预置了三条模拟一个已经进行中的对话。ForEach 遍历渲染消息气泡ForEach(this.messages,(msg:MessageItem){MessageBubble({sender:msg.sender,content:msg.content,isMe:msg.isMe})})这是组件通信的核心写法ForEach遍历messages数组对每条消息创建一个MessageBubble组件实例把消息数据作为Prop传入。当this.messages数组改变新增了一条消息ArkUI 的响应式机制会自动重新运行ForEach渲染出新的气泡。这就是状态驱动 UI——你只需要改状态UI 自动跟上。发送消息更新父组件状态Button(发送).margin({left:8}).onClick((){if(this.inputText.trim()){this.messages[...this.messages,{sender:我,content:this.inputText,isMe:true}]this.inputTextthis.unreadCount}})用展开运算符[...this.messages, 新消息]创建一个新数组新消息追加到末尾。为什么不用.push()而要创建新数组因为 ArkUI 的响应式系统检测的是引用变化——如果你直接.push()修改原数组引用地址没变ArkUI 不会触发重新渲染换成新数组赋值引用地址变了才能触发更新。this.inputText 清空输入框this.unreadCount更新未读计数这个案例里只是演示没有真正的已读/未读逻辑。未读消息角标Badge 组件Row(){Text(消息对话).fontSize(14).fontWeight(FontWeight.Medium).layoutWeight(1)Text(${this.unreadCount}条未读).fontSize(11).padding({left:8,right:8,top:2,bottom:2}).backgroundColor(#FF6B6B).fontColor(#FFFFFF).borderRadius(10)}.width(100%).margin({bottom:12})右上角的未读计数是一个手动实现的角标样式用红色背景 圆角的Text模拟。每次发送消息unreadCount加 1角标数字同步更新。这演示了同一个状态变量可以在页面多处地方都被使用——状态是单一数据源UI 是状态的多个视图。完整代码Componentstruct MessageBubble{Propsender:stringPropcontent:stringPropisMe:booleanfalsebuild(){Column(){Text(this.sender).fontSize(11).fontColor(#888888).margin({bottom:4})Text(this.content).fontSize(14).fontColor(this.isMe?#FFFFFF:#333333).padding(10).backgroundColor(this.isMe?#4D96FF:#F0F0F0).borderRadius(12).constraintSize({maxWidth:80%})}.alignItems(this.isMe?HorizontalAlign.End:HorizontalAlign.Start).width(100%).padding({left:this.isMe?40:8,right:this.isMe?8:40}).margin({bottom:8})}}EntryComponentstruct Demo{StateisShow:booleantrueStatemessages:MessageItem[][{sender:客服,content:您好请问有什么可以帮您?,isMe:false},{sender:我,content:我想了解下 ArkUI 组件通信,isMe:true},{sender:客服,content:好的组件通信主要通过 Prop、Link、事件回调等方式实现,isMe:false},]StateinputText:stringStateunreadCount:number1build(){Column(){if(this.isShow){Scroll(){Column(){Text(组件通信).fontSize(18).fontWeight(FontWeight.Bold).margin({bottom:8})Column(){Row(){Text(消息对话).fontSize(14).fontWeight(FontWeight.Medium).layoutWeight(1)Text(${this.unreadCount}条未读).fontSize(11).padding({left:8,right:8,top:2,bottom:2}).backgroundColor(#FF6B6B).fontColor(#FFFFFF).borderRadius(10)}.width(100%).margin({bottom:12})ForEach(this.messages,(msg:MessageItem){MessageBubble({sender:msg.sender,content:msg.content,isMe:msg.isMe})})Row(){TextInput({placeholder:输入消息...,text:this.inputText}).layoutWeight(1).onChange((value:string){this.inputTextvalue})Button(发送).margin({left:8}).onClick((){if(this.inputText.trim()){this.messages[...this.messages,{sender:我,content:this.inputText,isMe:true}]this.inputTextthis.unreadCount}})}.width(100%).margin({top:12})}.width(100%).backgroundColor(#FFFFFF).borderRadius(12).padding(16)}.width(100%)}.layoutWeight(1)}}.width(100%).height(100%).backgroundColor(#F5F6FA).padding(16)}}interfaceMessageItem{sender:stringcontent:stringisMe:boolean}ArkUI 组件通信方式汇总方式适用场景特点Prop父 → 子单向传值子组件只读父改变后子自动更新Link父子双向同步子组件修改会同步回父组件事件回调子 → 父通知父组件子组件调用父组件传入的函数Provide/Consume跨层级传递不需要一层层传 Prop直接隔代传值本文案例使用的是最基础的Prop单向传值适合父组件管数据子组件只负责显示的场景也是最推荐初学者先掌握的通信方式。总结组件通信是 ArkUI 开发的核心技能理解它需要先建立状态是单一数据源的思维方式。本文通过一个完整的聊天界面案例展示了最常见的组件通信模式父组件持有State状态通过Prop把数据传给子组件展示用户交互触发父组件状态更新UI 自动重新渲染。MessageBubble这个子组件的设计很值得体会它只接受Prop传入的三个参数通过isMe这一个布尔值就实现了完全不同的两种气泡样式这正是组件设计中简单接口、丰富表达的体现。子组件内部的样式逻辑越内聚外部使用起来就越简单。掌握了PropForEach这套组合就能搭建出绝大多数列表类、信息流类、数据展示类的页面是 ArkUI 开发效率提升的关键一步。