当前位置:首页>鸿蒙APP>鸿蒙 ArkTS UI 复用装饰器全解:@Builder/@BuilderParam/@Styles/@Extend 实战避坑指南

鸿蒙 ArkTS UI 复用装饰器全解:@Builder/@BuilderParam/@Styles/@Extend 实战避坑指南

  • 2026-07-26 08:18:06
鸿蒙 ArkTS UI 复用装饰器全解:@Builder/@BuilderParam/@Styles/@Extend 实战避坑指南

鸿蒙 ArkTS UI 复用装饰器全解:@Builder/@BuilderParam/@Styles/@Extend 实战避坑指南

摘要:在 HarmonyOS NEXT 开发中,UI 复用是提升开发效率的核心手段。本文深度解析 ArkTS 四大 UI 复用装饰器的原理、使用场景与易踩坑点,配套完整可运行代码,帮你彻底搞清楚"什么时候用哪个"。


前言:复用的困境

刚上手鸿蒙开发,很多同学会面临这样的困境:

同样的 Button 样式用了 10 次,每次都写一堆相同的 .backgroundColor().borderRadius().fontSize()……

改个颜色就得找 10 个地方,稍不注意就遗漏一处,改出 Bug。

ArkTS 提供了四个装饰器专门解决这个问题:@Builder、@BuilderParam、@Styles、@Extend。它们各司其职,却经常被混淆。今天我们从原理到实战,把这四个装饰器彻底搞清楚。


一、四者关系一图搞懂

UI 复用装饰器├── @Styles      → 复用通用样式(链式属性)├── @Extend      → 扩展特定组件样式(带参数)├── @Builder     → 复用 UI 结构(组件片段)└── @BuilderParam → 组件插槽(父传子的UI片段)

一句话区别:

  • 样式
    复用用 @Styles、@Extend
  • 结构
    复用用 @Builder、@BuilderParam

二、@Styles:提取公共样式

基本用法

@Styles 用于将多个通用样式属性提取成一个方法,减少重复代码。

// 定义通用卡片样式@StylesfunctioncardStyle() {  .width('100%')  .padding(16)  .backgroundColor('#FFFFFF')  .borderRadius(12)  .shadow({ radius: 8, color: '#1A000000', offsetX: 0, offsetY: 2 })}@Entry@Componentstruct StylesDemo {  build() {    Column({ space: 16 }) {      // 多个组件复用同一套样式      Column() {        Text('订单卡片').fontSize(16).fontWeight(FontWeight.Bold)        Text('¥299.00').fontSize(24).fontColor('#FF4D4F')      }      .cardStyle()  // ✅ 直接调用      Column() {        Text('地址卡片').fontSize(16).fontWeight(FontWeight.Bold)        Text('广东省深圳市南山区...').fontSize(14).fontColor('#666666')      }      .cardStyle()  // ✅ 复用相同样式    }    .padding(16)  }}

⚠️ 坑1:@Styles 不能带参数

// ❌ 错误!@Styles 方法不允许传参@Stylesfunction textStyle(color: string) {  // 编译报错  .fontColor(color)}// ✅ 需要传参时,改用 @Extend

⚠️ 坑2:@Styles 只能用于通用属性

@Stylesfunction buttonStyle() {  .width(120)  .height(44)  .stateEffect(true)  // ✅ 通用属性 OK  // .type(ButtonType.Capsule)  // ❌ Button 专属属性,@Styles 里不能用}

关键原则:@Styles 里只能写任何组件都支持的公共属性(width/height/padding/margin/backgroundColor 等),不能写特定组件的专属属性。


三、@Extend:扩展特定组件样式

@Extend 专门用于扩展某个特定组件的样式,支持传参,能使用该组件的专属属性。

基本用法

// 扩展 Text 组件:带参数的标题样式@Extend(Text)function titleStyle(size: number, color: string = '#333333') {  .fontSize(size)  .fontColor(color)  .fontWeight(FontWeight.Bold)  .lineHeight(size * 1.5)}// 扩展 Button 组件:主按钮样式@Extend(Button)function primaryBtn(width: number = 200) {  .type(ButtonType.Capsule)   // ✅ Button 专属属性  .width(width)  .height(44)  .backgroundColor('#4A90E2')  .fontColor('#FFFFFF')  .fontSize(16)  .stateEffect(true)}@Entry@Componentstruct ExtendDemo {  build() {    Column({ space: 20 }) {      Text('页面主标题').titleStyle(24)      Text('次级标题').titleStyle(18, '#666666')      Text('警告提示').titleStyle(14, '#FF4D4F')      Button('立即购买').primaryBtn()      Button('加入购物车').primaryBtn(160).backgroundColor('#FF7A00')  // ✅ 可链式覆盖    }    .padding(24)  }}

@Styles vs @Extend 对比

特性
@Styles
@Extend
目标
所有组件通用属性
特定组件(需指定)
参数
❌ 不支持
✅ 支持
专属属性
❌ 不能使用
✅ 可以使用
全局定义
✅
✅
组件内定义
✅(加 declare)
❌ 只能全局

⚠️ 坑3:@Extend 只能写在全局

@Componentstruct MyComp {  // ❌ 错误!@Extend 不能定义在组件内部  @Extend(Text)  function innerStyle() { .fontSize(14) }  build() { ... }}// ✅ 必须写在组件外(文件顶层)@Extend(Text)function innerStyle() { .fontSize(14) }

四、@Builder:可复用的 UI 片段

@Styles 和 @Extend 只能复用属性,而 @Builder 可以复用整段UI 结构(包含子组件、布局等)。

全局 @Builder

// 商品价格展示组件(全局定义)@BuilderfunctionPriceTag(price: number, originalPrice: number) {  Row({ space: 8 }) {    Text(`¥{originalPrice}`)      .fontSize(14)      .fontColor('#999999')      .decoration({ type: TextDecorationType.LineThrough })    Text(`省¥r('app.media.heart_filled'): r('app.media.product_img'))        .width('100%')        .aspectRatio(1)        .borderRadius(8)      // 调用局部 Builder      this.FavoriteIcon()        .margin({ top: 8, right: 8 })    }  }}

⚠️ 坑4:@Builder 中修改状态必须用 $$

ArkTS 中 @Builder 传参默认是值传递(按值),如果需要双向绑定,要用 ${num}`).onClick(() => {      num++  // 不会触发父组件更新!    })  }  // ✅ 引用传递:使用 

: { num: number }){    Button(`{this.count}`)    }  }}

五、@BuilderParam:组件插槽机制

@BuilderParam 是 ArkTS 的插槽(Slot)机制,允许父组件向子组件传入一段 UI 片段,类似 Vue 的 <slot> 或 React 的 children。

基础用法:单插槽

// 通用卡片容器(子组件)@Componentstruct GenericCard {  @BuilderParam content: () => void  // 声明插槽  @BuilderParam  defaultContent() {                  // 默认内容    Text('暂无内容').fontColor('#999999')  }  build() {    Column() {      // 渲染外部传入的 UI      this.content()    }    .width('100%')    .padding(16)    .backgroundColor('#FFFFFF')    .borderRadius(12)    .shadow({ radius: 8, color: '#1A000000', offsetX: 0, offsetY: 2 })  }}// 父组件使用@Entry@Componentstruct SlotDemo {  build() {    Column({ space: 16 }) {      // 使用尾随闭包语法传入内容(单个 @BuilderParam 时可用)      GenericCard() {        Row({ space: 12 }) {          Image({score}分`)      .fontSize(13)      .fontColor('#666666')      .margin({ left: 4 })  }}// ========== 可复用区块组件 ==========@Componentstruct InfoSection {  title: string = ''  @BuilderParam content: () => void  // 内容插槽  build() {    Column() {      // 区块标题      Row() {        Divider()          .vertical(true)          .height(16)          .color('#4A90E2')          .strokeWidth(3)          .margin({ right: 8 })        Text(this.title)          .fontSize(16)          .fontWeight(FontWeight.Bold)      }      .sectionPadding()      .backgroundColor('#F8F8F8')      // 插槽内容      Column() {        this.content()      }      .sectionPadding()    }    .width('100%')    .backgroundColor('#FFFFFF')    .margin({ bottom: 8 })  }}// ========== 页面组装 ==========@Entry@Componentstruct ProductDetailPage {  @State product: Record<string, string | number> = {    name: '华为 Mate X6 折叠屏手机',    price: 8999,    originalPrice: 9999,    score: 4.8,    sales: 12563  }  @Builder  BasicInfo() {    Column({ space: 8 }) {      // 标签行      Row({ space: 8 }) {        Text('官方正品').tagStyle()        Text('7天无理由').tagStyle('#E8F5E9', '#52C41A')        Text('48H发货').tagStyle('#E3F2FD', '#1890FF')      }      // 评分      StarRating(this.product.score as number)      // 销量      Text(`已售 r('app.media.product_main'))          .width('100%')          .aspectRatio(1)        // 价格区域        Row({ space: 12 }) {          Text(`¥{this.product.originalPrice}`)            .fontSize(16)            .fontColor('#BBBBBB')            .decoration({ type: TextDecorationType.LineThrough })        }        .sectionPadding()        .backgroundColor('#FFFFFF')        .width('100%')        // 基础信息区块        InfoSection({ title: '商品信息' }) {          this.BasicInfo()        }        // 规格选择区块        InfoSection({ title: '选择规格' }) {          this.Specs()        }      }    }    .width('100%')    .height('100%')    .backgroundColor('#F5F5F5')  }}

七、选择指南:一张表搞定

场景
推荐方案
原因
多个组件共享相同的 padding/margin/background
@Styles
通用属性,语法简洁
同类组件有带参数的共用样式(如不同颜色的按钮)
@Extend
支持参数,可用专属属性
多处复用相同的 UI 结构(一段布局/组合组件)
@Builder
复用 UI 片段而非样式
开发卡片/容器类组件,需要插槽能力
@BuilderParam
组件插槽,高度灵活

总结

ArkTS 这四个 UI 复用装饰器,核心逻辑是:

  1. @Styles
     → 没参数、只有通用属性时用它,最简单
  2. @Extend
     → 需要参数或组件专属属性时升级到它
  3. @Builder
     → 复用的是"一块UI"而不只是样式时用它
  4. @BuilderParam
     → 构建通用容器组件,需要调用方填充内容时用它

掌握了这四个装饰器,你的鸿蒙代码复用率能提升 40% 以上,改需求时再也不用全局搜索替换了。

如果文章对你有帮助,欢迎点赞收藏~下期打算写ArkTS 自定义渲染节点与 FrameNode 高性能绘制,感兴趣的可以关注!


参考资料:

  • HarmonyOS 官方文档:@Builder装饰器
  • HarmonyOS 官方文档:@BuilderParam装饰器
  • HarmonyOS 官方文档:@Styles装饰器
  • HarmonyOS 官方文档:@Extend装饰器

最新文章

随机文章