当前位置: 首页 > news >正文

【HarmonyOS实战】 @Builder构建函数:UI复用的正确姿势

文章目录

    • 前言
    • 一、@Builder 是什么?
    • 二、@Builder vs @Component
    • 三、项目中的三个 @Builder
      • 3.1 titleBuilder:标题栏
      • 3.2 bindBuilder:底部弹窗内容
      • 3.3 stationInfoCard:加油站卡片(带参数)
    • 四、全局 @Builder
    • 五、@Builder 的限制
      • 5.1 不能有独立的 @State
      • 5.2 参数传递限制
    • 六、什么时候用 @Builder,什么时候用 @Component?
    • 七、实际建议:如何拆分复杂页面
    • 总结

前言

打开GasStationPage.ets,你会发现有三个@Builder装饰的方法:stationInfoCardbindBuildertitleBuilder。这些方法都返回 UI,但又不是独立的@Component组件。

@Builder到底是什么?和组件有什么区别?什么时候用@Builder,什么时候用@Component?这篇文章讲清楚这些问题。

项目预览

一、@Builder 是什么?

@Builder是一个轻量级的 UI 复用机制,可以把一段 UI 代码抽取成一个"可调用的 UI 片段"。

@Entry@Componentstruct MyPage{// 定义 @Builder:把一段 UI 抽取出来@BuilderheaderBuilder(){Row(){Text('标题').fontSize(20).fontWeight(700)}.width('100%').height(50)}build(){Column(){this.headerBuilder()// 调用 @Builder,就像调用函数一样// 其他内容...}}}

二、@Builder vs @Component

特性@Builder@Component
定义方式函数(方法)结构体(struct)
状态管理共享父组件的状态独立的状态作用域
参数传递直接函数参数通过 @Prop/@Link 等
生命周期无独立生命周期有完整生命周期
复用粒度UI 片段,轻量独立组件,重量
适用场景复杂页面内部拆分跨页面复用的独立组件

核心区别@Builder里的 UI 是父组件的一部分,共享父组件的this(即可以直接访问父组件的状态变量);@Component是独立的组件,有自己的状态作用域。

三、项目中的三个 @Builder

3.1 titleBuilder:标题栏

@BuildertitleBuilder(){Row({space:Constants.SPACE_8}){Image($r('app.media.back')).width(Constants.IMAGE_BACK_WIDTH)// 40.height(Constants.IMAGE_BACK_HEIGHT)// 40.onClick(()=>{this.pageInfos.pop();// 直接访问父组件的 pageInfos!});Text($r('app.string.car_life')).fontWeight(Constants.FONT_WEIGHT_700).fontSize(Constants.FONT_SIZE_20).lineHeight(Constants.LINE_HEIGHT_27).fontColor($r('app.color.gas_station_name_color'));}.width(Constants.FULL_PERCENT).padding({left:Constants.PADDING_RIGHT_16}).position({top:Constants.POSITION_TOP});}

注意this.pageInfos.pop()——@Builder里直接访问父组件(GasStationPage)的pageInfos,这是@Component做不到的(子组件需要通过@Prop/@Link接收数据)。

为什么用 @Builder 而不是 @Component?
因为标题栏只在这一个页面用,而且需要直接访问页面的路由栈(pageInfos),用@Builder是最简单的方式。

3.2 bindBuilder:底部弹窗内容

@BuilderbindBuilder(){Column(){Scroll(){if(this.stationInfoList&&this.stationInfoList.length>0){List(){ForEach(this.stationInfoList,(station:StationData)=>{// 直接用 stationInfoListListItem(){Row({space:Constants.SPACE_12}){this.stationInfoCard(station);// 调用另一个 @Builder}.width(Constants.FULL_PERCENT);};},(station:StationData)=>{returnstation.id+station.name;});}.width(Constants.FULL_PERCENT);}}// ...样式}.height(Constants.MY_BUILDER_COLUMN_HEIGHT);}

@Builder里可以调用另一个 @Builderthis.stationInfoCard(station)就是在bindBuilder里调用了stationInfoCard

3.3 stationInfoCard:加油站卡片(带参数)

@BuilderstationInfoCard(gasStation:StationData):void{// 注意:有参数!Column({space:Constants.SPACE_12}){// 卡片内容....onClick(()=>{if(gasStation){this.openOrCloseMap(true);// 访问父组件方法this.moveToGasStation(gasStation.latitude,gasStation.longitude);// 访问父组件方法}else{this.getUIContext().getPromptAction().showToast({message:$r('app.string.Stay_tuned')});}});}}

@Builder可以有参数stationInfoCard接收一个StationData参数,每次调用传入不同的数据,渲染不同的卡片。

调用时:

this.stationInfoCard(station)// 传入当前循环的加油站数据

四、全局 @Builder

上面三个都是组件内部的局部 @Builder(作为组件的方法)。@Builder也可以定义在组件外部,成为全局 @Builder

// 文件顶层定义(全局)@BuilderexportfunctionGasStationPageBuilder(){GasStationPage();}// 可以在任何地方调用GasStationPageBuilder();

为什么GasStationPageBuilder是全局 @Builder?

因为它是 Navigation 路由系统的入口。route_map.json里配置了buildFunction: "GasStationPageBuilder",框架需要通过这个全局函数来创建页面,所以必须是全局可访问的(export function)。

全局@Builder和组件方法的区别:

  • 全局:不属于任何组件,没有this,不能访问组件状态
  • 局部(组件方法):属于组件,有this,可以访问组件的属性和方法

五、@Builder 的限制

5.1 不能有独立的 @State

@BuildermyBuilder(){@Statecount:number=0;// ❌ 报错!@Builder 里不能定义 @StateText(`${count}`)}

@Builder不能定义自己的状态变量。如果需要状态,要么:

  • 用父组件的状态变量(共享状态)
  • 把 UI 抽成真正的@Component

5.2 参数传递限制

// 简单类型参数:按值传递,父组件状态变化不会同步到 Builder 里@BuildermyBuilder(text:string){Text(text)// text 变化不会自动刷新(除非父组件重新调用)}// 对象类型参数:传引用可以观察到变化@BuildermyBuilder(config:{text:string}){Text(config.text)// config 对象里的属性变化可以被观察到}

提示:如果@Builder里的 UI 需要响应父组件某个值的变化,建议直接用this.xxx访问父组件的@State变量,而不是通过参数传递。

六、什么时候用 @Builder,什么时候用 @Component?

用 @Builder 当

  • UI 片段只在当前组件内使用
  • 需要直接访问父组件的状态变量
  • UI 片段不需要独立的状态和生命周期
  • 只是为了让代码更整洁,拆分大build()函数

用 @Component 当

  • UI 组件需要在多个页面复用
  • 组件需要独立的状态管理
  • 组件有复杂的生命周期(aboutToAppearonWillDisappear等)
  • 组件需要接受外部的数据绑定(@Prop@Link@ObjectLink

七、实际建议:如何拆分复杂页面

面对一个复杂页面,推荐这样的拆分策略:

GasStationPage(@Component @Entry) ├── titleBuilder(@Builder) ← 只此页面用,需要访问页面路由 ├── bindBuilder(@Builder) ← 只此页面用,需要访问页面数据 │ └── stationInfoCard(@Builder) ← 复杂卡片,提取为 Builder

如果stationInfoCard未来需要在其他地方复用,就把它抽成独立的@Component

// StationCard.ets@Componentstruct StationCard{@PropgasStation:StationData;onClickCallback:(station:StationData)=>void=()=>{};build(){// 卡片 UI}}

总结

@Builder是 ArkUI 中轻量的 UI 复用机制:

  1. 局部 @Builder(组件方法):共享父组件状态,可有参数,适合拆分同一页面的 UI 片段
  2. 全局 @Builder(文件顶层函数):无this,适合路由入口等全局调用场景
  3. vs @Component:@Builder 轻量但功能有限,@Component 独立但较重

GasStationPage用三个 @Builder 把复杂的build()方法拆分成几个有意义的部分,代码可读性大大提升。

下一篇讲LocationKit 定位服务——geoLocationManager是怎么获取用户位置的,异步获取位置有哪些注意事项。

http://www.gsyq.cn/news/1477494.html

相关文章:

  • SAP FIBF实战:手把手教你用BTE增强搞定会计凭证字段自动替换
  • 从WRF输出变量到天气分析:手把手教你用NCL提取关键气象要素(以一次暴雨过程为例)
  • 告别硬件SPI资源紧张:用GPIO模拟驱动ADS8684/8688的避坑指南与性能实测
  • Sobolev-Lorentz嵌入在Cartan-Hadamard流形上的最优性研究
  • 别再被‘抖振’劝退!用Python从零实现一个简单的滑模控制器(附完整代码)
  • 从论文拒稿到接收:LaTeX子图标签(label)和引用(ref)的避坑指南
  • 从Eclipse老手到STS新手:一份无缝迁移的避坑指南与个性化配置清单
  • Matlab鱼雷刚体运动仿真:俯仰/偏航/深度/航速四维动态可视化
  • ai一键生成vivado安装验证脚本,快速搭建fpga开发环境
  • 从数据到洞察:手把手教你用Python处理卫星测高数据计算SLA/SSHA
  • MicroPython固件“魔改”指南:以BLACK_F407ZG为例,自定义你的板载LED、串口和SPI引脚
  • 别再手动试错了!用Minitab 21做全因子DOE,5步搞定工艺参数优化
  • 瑞萨e2 studio调试配置全解析:Connection Settings里那个200mA选项到底该不该勾?
  • 告别环境冲突:用Docker一键部署Matconvnet(支持Matlab 2020b + CUDA 11)
  • AI虚拟城市主义:生成式模型与城市身份量化分析
  • 别再死记硬背了!用Proteus 8.9仿真51单片机,手把手教你搭建第一个流水灯电路
  • 物理信息神经网络与随机增广拉格朗日方法解析
  • 3分钟掌握Keyviz:让屏幕操作从此不再神秘
  • 从零开始搞懂SoC:芯片设计中的‘大脑’与‘高速公路’(AMBA总线篇)
  • 从《半日》到代码人生:一个程序员如何用技术工具高效啃下大学英语精读(附Anki+欧路词典配置)
  • 从赌徒破产到网页排名:齐次马尔可夫链在算法面试中的高频考点解析
  • 实战指南:基于快马生成的php应用骨架,快速构建企业级内容管理系统
  • 用Arduino Uno和PAJ7620U2手势传感器做个智能灯控:从接线到代码调试的完整避坑指南
  • 概率密度函数与区域核:概念、验证与应用
  • 前端打印PDF踩坑记:C-Lodop加载远程PDF链接为何打印空白?附完整解决方案
  • 别再直接用经纬度了!用Python的mgtwr包做GTWR建模,手把手教你处理时空数据的正确姿势
  • 从屏幕到代码:ColorWanted免费取色器的终极指南
  • 别只盯着64 GT/s!盘点PCIe 6.0那些可能更影响你实际项目的‘隐形’特性:FLIT、L0p与纠错
  • 从Oracle/MySQL转战国产库?手把手带你快速上手人大金仓Kingbase核心操作
  • 用BC547C三极管做个触摸开关?从达林顿管到单管电路的波形实测与选型建议