news 2026/6/6 18:32:25

组件通信——@Prop 与 @Link 父子传值完全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
组件通信——@Prop 与 @Link 父子传值完全指南

文章目录

    • 前言
    • 一、什么是父子组件?
      • 1.1 组件树的概念
      • 1.2 基本的组件拆分示例
    • 二、@Prop:父到子的单向传递
      • 2.1 @Prop 的基本规则
      • 2.2 @Prop 传递复杂对象
    • 三、@Link:父子双向绑定
      • 3.1 @Link 的使用场景
      • 3.2 @Link 的关键语法:`$$` 运算符
    • 四、回调函数通信:子组件通知父组件
      • 4.1 使用函数参数传递回调
    • 五、三种通信方式总结对比
    • 总结

前言

随着项目越来越复杂,UI 代码不可能全写在一个组件里。我们需要把界面拆分成多个子组件,然后通过参数传递让父子组件协同工作。HarmonyOS ArkUI 提供了@Prop(单向传递)和@Link(双向绑定)两种机制,满足不同场景的通信需求。

本篇通过真实可运行代码,带你掌握父子组件通信的所有核心技巧。

一、什么是父子组件?

1.1 组件树的概念

在 ArkUI 中,每个@Component就是一个组件。组件可以嵌套,形成组件树

MainPage(父) └── Navigation └── GasStationPage(子) ├── MapComponent(子) ├── titleBuilder(内联 @Builder) └── bindBuilder(内联 @Builder) └── stationInfoCard(内联 @Builder,相当于子组件)

父组件通过属性(自定义参数)向子组件传递数据,子组件通过回调函数@Link向父组件反馈状态变化。

1.2 基本的组件拆分示例

把一个复杂卡片拆分为独立组件:

// 子组件:加油站卡片@Componentstruct StationCard{// 用 @Prop 接收父组件传来的数据(单向,父→子)@PropstationName:string='';@PropstationAddr:string='';@Propdistance:string='';build(){Row({space:12}){// 左侧图标Image($r('app.media.startIcon')).width(48).height(48).borderRadius(8)// 右侧信息Column({space:4}){Text(this.stationName).fontSize(16).fontWeight(FontWeight.Medium).fontColor('#333333')Text(this.stationAddr).fontSize(13).fontColor('#999999')}.alignItems(HorizontalAlign.Start).layoutWeight(1)// 距离显示Text(`${this.distance}km`).fontSize(14).fontColor('#1A6FF5')}.width('100%').padding(16).backgroundColor('#FFFFFF').borderRadius(12)}}// 父组件:使用子组件@Entry@Componentstruct ParentPage{build(){Column({space:12}){// 像 HTML 属性一样传值StationCard({stationName:'中国石化加油站',stationAddr:'北京市海淀区中关村大街1号',distance:'1.2'})StationCard({stationName:'中国石油加油站',stationAddr:'北京市朝阳区建国路88号',distance:'2.5'})}.padding(16).width('100%')}}

二、@Prop:父到子的单向传递

2.1 @Prop 的基本规则

特性说明
数据流向父组件 → 子组件(单向)
子组件能否修改能,但不影响父组件
父组件更新时子组件的 @Prop 值同步更新
初始值必须提供默认值
@Componentstruct ChildWithProp{@Proptitle:string='默认标题';// 父组件传来,子组件可读@PropisActive:boolean=false;build(){Text(this.title).fontColor(this.isActive?'#1A6FF5':'#999999').fontSize(16)}}

2.2 @Prop 传递复杂对象

// 定义数据类型classCardInfo{title:string='';subtitle:string='';iconColor:string='#1A6FF5';constructor(title:string,subtitle:string,iconColor?:string){this.title=title;this.subtitle=subtitle;if(iconColor){this.iconColor=iconColor;}}}@Componentstruct InfoCard{@PropcardInfo:CardInfo=newCardInfo('','');build(){Column({space:8}){Row(){Circle().width(12).height(12).fill(this.cardInfo.iconColor)Text(this.cardInfo.title).fontSize(16).fontWeight(FontWeight.Bold).margin({left:8})}Text(this.cardInfo.subtitle).fontSize(13).fontColor('#666666')}.padding(16).width('100%').backgroundColor('#F8F9FA').borderRadius(12).alignItems(HorizontalAlign.Start)}}@Entry@Componentstruct PropObjectDemo{@Statecards:CardInfo[]=[newCardInfo('附近加油站','为您找到 5 个加油站','#1A6FF5'),newCardInfo('最近加油站','距您 1.2km','#52C41A'),newCardInfo('优惠信息','今日 92# 汽油立减 0.1元/升','#FA8C16'),];build(){Column({space:12}){ForEach(this.cards,(card:CardInfo)=>{InfoCard({cardInfo:card})})}.padding(16).width('100%')}}

提示:传递对象时,@Prop 接收的是父组件对象的深拷贝,子组件修改不影响父组件。

三、@Link:父子双向绑定

3.1 @Link 的使用场景

子组件的操作需要改变父组件的状态时,使用@Link。比如:子组件中有一个开关,切换后要同步到父组件的状态变量。

// 子组件:带 @Link 的开关@Componentstruct ToggleSwitch{@LinkisEnabled:boolean;// 双向绑定父组件的变量build(){Row({space:12}){Text(this.isEnabled?'已开启':'已关闭').fontSize(14).fontColor(this.isEnabled?'#52C41A':'#999999')Toggle({type:ToggleType.Switch,isOn:this.isEnabled}).onChange((isOn:boolean)=>{this.isEnabled=isOn;// 修改 @Link 变量,父组件同步更新!}).selectedColor('#1A6FF5')}.width('100%').justifyContent(FlexAlign.SpaceBetween).padding({left:16,right:16,top:12,bottom:12}).backgroundColor('#FFFFFF').borderRadius(12)}}// 父组件@Entry@Componentstruct LinkDemo{@StatelocationEnabled:boolean=false;@StatenotifyEnabled:boolean=true;build(){Column({space:12}){// 状态展示Column({space:4}){Text('当前设置状态:').fontSize(14).fontColor('#999999')Text(`定位:${this.locationEnabled?'开':'关'}| 通知:${this.notifyEnabled?'开':'关'}`).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')}.width('100%').padding(16).backgroundColor('#F5F7FA').borderRadius(12)Text('应用设置').fontSize(18).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start)// 传入 $变量名($$运算符)表示 @Link 绑定ToggleSwitch({isEnabled:$locationEnabled})ToggleSwitch({isEnabled:$notifyEnabled})}.padding(16).width('100%')}}

3.2 @Link 的关键语法:$$运算符

使用@Link时,父组件传参时要在变量名前加$

// 父组件传递 @Link 参数ChildComponent({myLinkVar:$parentStateVar})// ↑ 注意这个 $ 符号// 对比 @Prop 传递(不加 $)ChildComponent({myPropVar:this.parentStateVar})

在本项目中,bindSheet也用了类似语法:

.bindSheet($$this.isShow,this.bindBuilder(),{// ...})

$$this.isShow表示将isShow双向绑定的方式传给bindSheet,弹窗内部关闭时会自动修改isShowfalse

四、回调函数通信:子组件通知父组件

4.1 使用函数参数传递回调

除了 @Link,还可以通过函数回调实现子→父通信:

@Componentstruct CounterButton{@Proplabel:string='按钮';// 回调函数:父组件传入,子组件调用onCountChange:(count:number)=>void=()=>{};@Stateprivatecount:number=0;build(){Button(`${this.label}:${this.count}`).onClick(()=>{this.count++;this.onCountChange(this.count);// 通知父组件}).backgroundColor('#1A6FF5').fontColor('#FFFFFF').borderRadius(20).padding({left:20,right:20,top:10,bottom:10})}}@Entry@Componentstruct CallbackDemo{@StatetotalCount:number=0;@Statelog:string[]=[];build(){Column({space:16}){Text(`总计:${this.totalCount}`).fontSize(24).fontWeight(FontWeight.Bold)Row({space:12}){CounterButton({label:'加油站A',onCountChange:(count:number)=>{this.totalCount+=1;this.log=[...this.log,`A 被点击了${count}`];}})CounterButton({label:'加油站B',onCountChange:(count:number)=>{this.totalCount+=1;this.log=[...this.log,`B 被点击了${count}`];}})}// 显示最近3条日志Column({space:4}){ForEach(this.log.slice(-3),(item:string)=>{Text(item).fontSize(13).fontColor('#666666')})}}.padding(24).width('100%').height('100%').justifyContent(FlexAlign.Center)}}

五、三种通信方式总结对比

父 ──── @Prop ────→ 子 单向,子修改不影响父 父 ←─── @Link ────→ 子 双向,子修改同步到父 父 ←─── callback ── 子 子主动调用父提供的函数
通信方式适用场景语法特点
@Prop展示数据,子组件不需要反馈父:comp({ x: this.val })
@Link子组件需要修改父组件状态父:comp({ x: $val })
回调函数子组件事件通知父组件父:comp({ onEvent: () => {} })

提示:选择通信方式的原则——数据从哪来就从哪管。父组件拥有的数据,用 @Prop 传下去;需要双向同步,用 @Link;子组件触发事件,用回调函数。

总结

父子组件通信是构建可复用、可维护 UI 组件的核心技能。@Prop用于数据"向下流",@Link用于状态"双向绑定",回调函数用于事件"向上冒泡"。三种方式各有适用场景,灵活组合才能写出清晰解耦的组件树。下一篇我们来深入研究条件渲染和组件可见性控制。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 18:31:22

零基础学ui-ux设计:用快马生成可交互登录页,直观理解pro-max规范

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 我是一个设计新手,想学习如何构建一个简洁的登录注册页面。请生成一个包含以下元素的页面:1、一个简洁的卡片布局,居中显示。2、卡片内有“登录…

作者头像 李华
网站建设 2026/6/6 18:29:42

RuoYi项目接口文档丑?教你用Swagger-Bootstrap-UI一键换肤(附避坑指南)

RuoYi项目Swagger文档美化实战:从默认界面到专业级API门户每次打开RuoYi项目中那个灰蒙蒙的Swagger默认界面时,总有种打开十年前老式文档的错觉。作为长期使用RuoYi框架的后端开发者,我深知优秀的API文档不仅要功能完备,更需要具备…

作者头像 李华
网站建设 2026/6/6 18:29:25

ModelSim仿真进阶:从环境搭建到自动化验证的实战指南

1. 从资料整理到深度实践:我的ModelSim仿真学习路径复盘在数字电路设计,尤其是FPGA/CPLD开发领域,ModelSim(或者说其商业版本QuestaSim)几乎是工程师绕不开的仿真工具。我记得自己刚入门时,面对满屏的波形和…

作者头像 李华
网站建设 2026/6/6 18:29:13

开漏与开集电路设计:原理、应用与上拉电阻选型指南

1. 开漏与开集:从模糊概念到设计利器的深度解析在电路设计,尤其是数字电路、MCU/嵌入式系统以及各种总线接口设计中,“开漏”和“开集”这两个词就像空气一样无处不在,却又常常被我们习以为常地忽略其深层原理。很多工程师和我一样…

作者头像 李华
网站建设 2026/6/6 18:29:03

STM32内部Flash变身微型U盘:5分钟实现免驱配置存储方案

1. 项目概述与核心价值 最近在做一个嵌入式设备的小项目,需要让设备在连接电脑时能自动安装驱动,或者让用户能通过一个简单的配置文件来调整设备参数。常规思路是搞个外置的EEPROM或者SD卡,但总觉得为了这点小事增加BOM成本和PCB面积有点“杀…

作者头像 李华
网站建设 2026/6/6 18:24:23

GD32F407从官方例程到个人项目:手把手移植并优化你的第一个MDK工程

GD32F407从官方例程到个人项目:手把手移植并优化你的第一个MDK工程当你第一次拿到GD32F4xx官方固件库时,可能会被里面繁杂的文件夹和文件搞得晕头转向。官方例程就像是一个装满各种工具的大箱子,而我们需要的是从中挑选出真正需要的几件趁手工…

作者头像 李华