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

HarmonyOS掌上记账APP开发实践第28篇:自定义组件封装策略 — 从通用 Header 到业务组件的分层设计

自定义组件封装策略 — 从通用 Header 到业务组件的分层设计

一、引言

在大型应用中,合理的组件化设计是保证代码可维护性和复用性的基石。鸿蒙的 ArkTS 通过@ComponentV2装饰器支持自定义组件,允许开发者将页面拆分为可复用的独立模块。MoneyTrack 项目从通用基础组件(如 CommonHeader)到业务领域组件(如 AssetSummaryCard、AssetCard),构建了一套清晰的组件分层体系,实现了代码的高复用和低耦合。

二、核心知识点

2.1 @ComponentV2 与 @Component(V1) 对比

鸿蒙 ArkTS 提供了两代组件装饰器。@ComponentV2(推荐)相较于@Component(V1)在响应式和性能上有显著提升:

对比维度@ComponentV2@Component(V1)
数据装饰器@Param / @Local / @Computed@State / @Prop / @Link
响应式粒度属性级,精准触发更新对象引用级,整体替换才触发
性能表现仅更新变动的属性整个组件重建
语法简洁度不需要 .build() 内部赋初值略繁琐
推荐程度强烈推荐(新项目应使用)兼容旧项目
// @ComponentV2 写法(推荐)@ComponentV2exportstruct MyComponent{@Paramcount:number=0@LocalisVisible:boolean=truebuild(){/* ... */}}// @Component(V1) 写法(旧)@Componentexportstruct OldComponent{@Statecount:number=0@PropisVisible:boolean=truebuild(){/* ... */}}

2.2 @Param 与 @Local 的角色分配

@ComponentV2中,数据装饰器的选择决定了属性的职责:

  • @Param:接收父组件传入的外部属性(即 props),由父组件驱动更新。子组件不应修改@Param属性。
  • @Local:组件内部管理的私有状态,仅供组件自身使用,外界不可直接赋值。
@ComponentV2exportstruct UserCard{@ParamuserName:string=''// 父组件传入,只读@ParamavatarUrl:string=''// 父组件传入,只读@LocalisExpanded:boolean=false// 组件内部状态@LocallikeCount:number=0// 组件内部状态build(){Column(){Text(this.userName).fontSize(16)if(this.isExpanded){Text('详细资料...')}}}}

分配原则:凡是从父组件传入的数据用@Param,组件自己管理的交互状态用@Local

2.3 组件通信方式

自定义组件之间通过单向数据流进行通信:

  • 父→子:通过@Param属性传递数据
  • 子→父:通过回调函数(事件)通知父组件
// 父组件使用@ComponentV2exportstruct ParentPage{@Localitems:number[]=[1,2,3]build(){Column(){ForEach(this.items,(item:number)=>{ChildCard({value:item,onDelete:(id:number)=>{this.items=this.items.filter(v=>v!==id)}})})}}}// 子组件定义@ComponentV2exportstruct ChildCard{@Paramvalue:number=0@ParamonDelete:(id:number)=>void=()=>{}build(){Row(){Text(`${this.value}`)Button('删除').onClick(()=>this.onDelete(this.value))}}}

这种模式确保了数据从父到子单向流动,子组件通过回调通知父组件执行修改,避免了双向绑定的混乱。

2.4 组件封装的最佳粒度

组件拆分需要遵循"高内聚、低耦合"原则。MoneyTrack 的组件分为三个层次:

App(应用根节点)

TabBar(底部导航)

BillPage(账单页)

StatisticsPage(统计页)

MinePage(我的页)

CommonHeader(通用标题栏)

DailyBillGroup(每日分组)

BillCard(账单卡片)

CategoryIcon(分类图标)

ChartSummary(图表摘要)

CalendarSummary(日历摘要)

MemberCard(会员卡片)

SettingItem(设置项)

经验法则

  1. 单一职责:一个组件只做一件事。BillCard只负责展示单条账单,不负责布局列表。
  2. 接口归一化:组件的@Param应设计为统一的 Model 类型,而非零散的基础类型。
  3. props 不可变性:子组件应只读地消费@Param,不直接修改父组件数据。

2.5 CommonHeader 完整实现

以下是 MoneyTrack 中 CommonHeader 的完整代码:

@ComponentV2exportstruct CommonHeader{@Paramtitle:string=''@ParamonBack:()=>void=()=>{}@ParamrightText:string=''@ParamonRightClick:()=>void=()=>{}@ParamisDarkMode:boolean=falsebuild(){Row(){// 左侧返回按钮Image($r('app.media.ic_back')).width(24).height(24).onClick(()=>this.onBack())// 中间标题Text(this.title).fontSize(18).fontWeight(FontWeight.Bold).fontColor(this.isDarkMode?'#FFFFFF':'#333333')Blank()// 右侧可选操作if(this.rightText.length>0){Text(this.rightText).fontSize(14).fontColor('#007AFF').onClick(()=>this.onRightClick())}}.padding({left:16,right:16,top:12,bottom:12}).width('100%').backgroundColor(this.isDarkMode?'#1A1A1A':'#FFFFFF')}}

三、项目代码案例

3.1 CommonHeader 通用标题栏

products/entry/src/main/ets/components/CommonHeader.ets中,CommonHeader 封装了统一的导航栏。该组件在 BillDetailPage、AssetDetailPage、SettingPage 等多个页面中复用,保证了导航栏的视觉统一。

3.2 AssetSummaryCard / AssetCard 业务卡片

在资产模块中,业务卡片组件封装了资产数据展示的公共逻辑:

  • AssetSummaryCard:展示资产总览摘要(总资产、净资产等汇总指标)
  • AssetCard:展示单个资产项目名称、金额、账期信息

这些组件通过@Param接收AssetGroupModelAssetItemModel数据对象,渲染出统一的卡片 UI。

3.3 组件定义示例

@ComponentV2exportstruct AssetCard{@ParamassetData:AssetGroupModel=newAssetGroupModel()@ParamonTap:(id:number)=>void=()=>{}build(){Column(){Text(this.assetData.name).fontSize(16).fontWeight(FontWeight.Bold)Text(`¥${this.assetData.amount.toFixed(2)}`).fontSize(24)}.gesture(TapGesture().onAction(()=>{this.onTap(this.assetData.id)})).padding(16).backgroundColor(Color.White).borderRadius(12)}}

四、最佳实践总结

  1. 优先使用 @ComponentV2:新一代装饰器性能更好、语法更简洁。
  2. @Param 与 @Local 职责分明:外部数据用@Param,内部状态用@Local
  3. 单向数据流:父传子通过 props,子传父通过回调。
  4. 组件粒度适中:过细则文件碎片化,过粗则不可复用。
  5. 保持 props 不可变:子组件不修改传入的数据,确保数据流可预测。
  6. 接口归一化:推荐传入完整 Model 对象,而非零散字段。

五、参考文档

  • 自定义组件最佳实践
  • @ComponentV2 装饰器说明
  • @Param 装饰器
http://www.jsqmd.com/news/1208720/

相关文章:

  • 《超简单:用 Python 让 Excel 飞起来》读书笔记:9.1.2 导入并调用 Python 自定义函数
  • AI智能体GUI测试框架:构建、评估与实战部署指南
  • CatSeedLogin实战指南:5个关键步骤构建Minecraft服务器安全防线
  • CM32M433R-START开发板:RISC-V生态与物联网应用实战
  • VSCode配置Zephyr开发环境:实现STM32F103C8T6精准代码跳转
  • 2026不停车超限检测系统品牌排行榜,广州聚杰稳居行业前列 - 品牌速递
  • Dev-C++为何仍是C/C++入门首选?教育场景下的最小可行学习系统
  • 2026年 燃气无缝钢管实力厂家:高品质防腐/耐高压管道,工程级专业源头供应商深度解析 - 甄选服务推荐
  • 稳压二极管与普通二极管的核心差异与应用解析
  • AI Agent开发中的PLAN-AND-EXECUTE模式解析与实践
  • 第7章:分页、排序与游标——订单列表怎么越翻越慢
  • HarmonyOS掌上记账APP开发实践第29篇:条件渲染与 ForEach — 动态 UI 的构建策略
  • (12/99)汽车电子--体系
  • 突破平台界限:用Xenia Canary在PC上重温Xbox 360游戏的技术秘籍
  • 浪琴官方售后服务中心电话和完整维修地址实地考察报告多信源验证(2026年7月更新) - 浪琴服务中心
  • 2026超限运输检测仪品牌排行榜,广州聚杰稳居行业前列 - 品牌速递
  • 【Cursor国际化实战指南】:20年资深工程师亲授多语言支持从0到1落地的7大避坑法则
  • 2026不停车称重系统厂家推荐,广州聚杰是头部品牌 - 品牌速递
  • Ubuntu手动编译Linux内核全流程指南
  • Flash Attention V3:大模型训练加速的核心技术解析
  • 大型语言模型(LLM)幻觉问题综述:归因理论与缓解技术进展(2023–2025)
  • SpringBoot应用构建XSS与SQL注入双重防御体系实战
  • 如何快速多开暗黑破坏神2重制版:D2RML完整指南
  • 上海钻石回收门店怎么选?性价比深度对比 - 讯息早知道
  • Cursor AB测试代码工程化实践(从本地调试到生产灰度的7步标准化流程)
  • 华为/阳光/锦浪逆变器接入:云端API对接 vs SunSpec边缘解析(附协议配置)
  • HarmonyOS7 富文本混排页实战:RichText 与 Span 不只是会用,还要用得顺手
  • Win10官方ISO下载指南与安全安装全攻略
  • Autorize插件安装教程(Burp Suite)
  • 2026年包装海绵产业格局与鹤山市溢盛包装制品有限公司供应能力解析 - 甄选服务推荐