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

HarmonyOS7 自定义组件封装:从重复代码到优雅复用只需要3步

文章目录

      • 前言
      • 重复代码的痛
      • 3 步封装法
        • 第一步:提取结构
        • 第二步:定义接口
        • 第三步:完善逻辑
      • 使用封装好的 Card 组件
      • 组件设计原则
      • 写在最后

前言

上个月接手一个项目,打开代码一看——5 个页面,每个页面都有一段几乎一样的卡片 UI 代码,复制粘贴改改文字就完事。改需求的时候更惨,5 个地方都得改一遍,漏改一个就 bug。

重复代码是技术债的起点,而组件封装就是还债最快的方式。

这篇文章分享我总结的 3 步封装法,再通过一个完整的 Card 组件实战,带你从重复代码走到优雅复用。

重复代码的痛

重复代码不只是"看着不爽"的问题,它有三重罪:

  1. 改一处漏一处—— 5 个地方一样的代码,改需求只改了 4 个,第 5 个就是 bug

  1. 代码膨胀—— 本来 200 行能搞定的事,写了 800 行
  2. 不敢重构—— 重复代码太多,牵一发动全身,谁都不敢动

说白了,不封装 = 每次改动都是定时炸弹。

3 步封装法

第一步:提取结构

把重复的 UI 结构抽出来,做成独立的@Component

封装前:每个页面直接写卡片结构

// 页面AColumn(){Image($r('app.media.pic1')).width(60).height(60).borderRadius(8)Text('标题A').fontSize(16).fontWeight(FontWeight.Bold)Text('描述A').fontSize(13).fontColor('#999')}.padding(16).backgroundColor('#fff').borderRadius(12)

封装后:抽成一个组件,结构固定

@Componentstruct Card{build(){Column(){Image($r('app.media.pic1')).width(60).height(60).borderRadius(8)Text('标题A').fontSize(16).fontWeight(FontWeight.Bold)Text('描述A').fontSize(13).fontColor('#999')}.padding(16).backgroundColor('#fff').borderRadius(12)}}

这一步只是"搬了个家",数据还是写死的。但已经有价值了——改结构只改一处。

第二步:定义接口

把写死的数据改成参数,让组件可配置。这就是组件的"接口"。

改造后:

@Componentstruct Card{@Propicon:Resource=$r('app.media.default')@Proptitle:string=''@Propdesc:string=''build(){Column(){Image(this.icon).width(60).height(60).borderRadius(8)Text(this.title).fontSize(16).fontWeight(FontWeight.Bold)Text(this.desc).fontSize(13).fontColor('#999')}.padding(16).backgroundColor('#fff').borderRadius(12)}}

关键代码讲解:

  • @Prop icon: Resource—— 图片用Resource类型,兼容$r()和网络图片
  • @Prop title: string = ''—— 默认值设为空串,避免不传参时显示 undefined
  • @Prop而不是@State,因为数据来自父组件,组件内部不需要拥有

这一步之后,组件就活了——传不同的参数,展示不同的内容。

第三步:完善逻辑

加入事件回调、条件渲染、样式定制等能力,让组件真正通用。

最终版本:

@Componentstruct Card{@Propicon:Resource=$r('app.media.default')@Proptitle:string=''@Propdesc:string=''@PropshowArrow:boolean=trueonCardClick?:()=>voidbuild(){Row(){Image(this.icon).width(48).height(48).borderRadius(8).objectFit(ImageFit.Cover)Column(){Text(this.title).fontSize(16).fontWeight(FontWeight.Bold).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})Text(this.desc).fontSize(13).fontColor('#999').margin({top:4}).maxLines(2).textOverflow({overflow:TextOverflow.Ellipsis})}.layoutWeight(1).alignItems(HorizontalAlign.Start).margin({left:12})if(this.showArrow){Image($r('sys.media.ohos_ic_public_arrow_right')).width(16).height(16).fillColor('#ccc')}}.width('100%').padding(16).backgroundColor('#fff').borderRadius(12).onClick(()=>{if(this.onCardClick){this.onCardClick()}})}}

逐行讲解关键逻辑:

  • layoutWeight(1)—— 中间文字区域占满剩余空间,图标和箭头固定大小
  • maxLines(1)+textOverflow—— 标题超长时显示省略号,不会撑爆布局
  • if (this.showArrow)—— 条件渲染,不需要箭头的场景传showArrow: false
  • onCardClick?: () => void—— 可选回调,点击卡片时通知父组件
  • onClick里的空值检查—— 回调是可选的,不传就不触发,避免运行时崩溃

使用封装好的 Card 组件

对比一下,封装前 vs 封装后:

封装前(页面里重复写):

// 页面A、页面B、页面C... 每个都写一坨Row(){Image(...).width(48).height(48)Column(){Text(...);Text(...)}Image(箭头).width(16)}.padding(16).borderRadius(12).onClick(...)

封装后(一行搞定):

Card({icon:$r('app.media.settings'),title:'设置',desc:'应用设置与偏好',onCardClick:()=>{this.routerTo('Settings')}})

代码量从 10+ 行变成 1 行,改样式只改组件本身。这就是封装的价值。

组件设计原则

封装组件不是越多越好,记住这几条:

  • 两次重复就可以考虑封装—— 别等到第五次,第二次就该动手
  • 接口要少而精—— 一个组件超过 8 个参数就该拆了
  • 提供合理默认值—— 让最常用的场景可以少传参
  • 回调用可选属性——onXxx?: () => void,不传不报错
  • 样式也能参数化—— 不只是数据,圆角大小、内边距这些也可以暴露
  • 别过度封装—— 只出现一次的 UI 不用抽组件,强行封装反而增加理解成本

写在最后

组件封装这件事,说白了就是用一次的时间投入,换以后每次的省心

3 步封装法——提取结构、定义接口、完善逻辑——听起来简单,但很多人卡在第一步就不愿意动了。毕竟复制粘贴是最快的,封装要动脑子。

但相信我,下次需求变更的时候,你会感谢今天花 10 分钟封装的自己。

http://www.jsqmd.com/news/1233175/

相关文章:

  • AI算力优化:从芯片到系统的技术演进
  • Unity UGUI拼图游戏开发:从事件系统到交互逻辑的完整实现
  • 深入解析C2000 ePWM高级功能:斩波、故障保护与数字比较实战
  • C2000 I2C驱动开发:从寄存器到DriverLib的实战解析
  • 改了几个内核参数,服务就崩了?网络调优从来不是“玄学盲盒”
  • 保山市龙陵县2026最新黄金回收门店及联系方式指南 黄金回收白银回收铂金回收店铺TOP5排行榜 - 盛世金银回收
  • Tegaki:前端手写动画库的原理与应用
  • AI工程师必写的三份Terraform文件:main.tf、variables.tf、outputs.tf
  • 3个IO口驱动256个LED的动态扫描与74HC595方案
  • 工业现场疑难软故障实录:07 最难查的问题,往往藏在“偶发”里
  • 计算机毕业设计之MOOC平台上学生情绪倾向分析研究-以大数据相关课程为例
  • ARM中断控制器(AINTC)原理与实战:从优先级仲裁到向量化优化
  • 汽车SoC成像子系统架构解析:从硬件加速到多路视频处理实战
  • 抚州市乐安县2026最新黄金回收门店及联系方式指南 黄金回收白银回收铂金回收店铺TOP5排行榜 - 大熊猫898989
  • 巴中市南江县2026最新黄金回收门店及联系方式指南 黄金回收白银回收铂金回收店铺TOP5排行榜 - 盛世金银回收
  • 2026亲测有效教程:作业太多页怎么拍成一张图发给老师 - 效率工具研究所
  • GitHub顶级AI贡献者:技术风向标与实战指南
  • HarmonyOS7 Deep Link:App Linking 让你的应用跳转无处不在
  • 小程序商城开发从哪里开始?六步梳理功能与上线流程
  • 巴中市通江县2026最新黄金回收门店及联系方式指南 黄金回收白银回收铂金回收店铺TOP5排行榜 - 盛世金银回收
  • LangChain三层架构解析:从开发框架到生产部署
  • Unity游戏集成本地语音识别:Qwen3-ASR-1.7B实时控制实战
  • 亳州市利辛县2026最新黄金回收门店及联系方式指南 黄金回收白银回收铂金回收店铺TOP5排行榜 - 大熊猫898989
  • 【知识讲解】 链式哈希表的实现与unordered_map和unordered_set的封装
  • 抚顺市本溪市2026最新黄金回收门店及联系方式指南 黄金回收白银回收铂金回收店铺TOP5排行榜 - 盛世金银回收
  • 小程序智能体接入实战:轻量级AI集成方案
  • OpenSSL 3.2实战:生成与验证后量子双签名X.509证书
  • 深圳旧房改造装修公司怎么选初心装饰装修定制一体化更省心 - 优企甄选
  • 抚州市黎川县2026最新黄金回收门店及联系方式指南 黄金回收白银回收铂金回收店铺TOP5排行榜 - 大熊猫898989
  • AI大模型学习路线:从入门到精通的系统化路径