ArkTS 进阶之道(17):@Extend 专属属性扩展边界——为啥能收 fontSize 专属属性

ArkTS 进阶之道(17):@Extend 专属属性扩展边界------为啥能收 fontSize 专属属性

本文是「ArkTS 进阶之道」系列第 17 篇,续「ArkUI 组件设计」阶段。上一篇讲 @Styles 绑组件通用属性复用样式集(篇 65)------@Styles 只支持通用属性不支持专属属性+不支持参数。本文讲专属属性扩展边界:@Extend 荬饰器绑特定组件专属属性+支持参数 ------根因在绑特定组件属性槽不是通用属性集,@Extend 荬饰器绑特定组件(Text/Button 等)专属属性+支持参数动态调,补 @Styles 不支持专属属性/参数的边界。能力系列篇 19 讲过 @Builder 怎么用,本文讲为哈 @Extend 收专属属性合法 @Styles 收专属属性报错------根因在特定组件属性槽绑定不是通用属性集。

一、开篇:@Extend 不是通用属性集,是绑特定组件专属属性的扩展荬饰器

你写 TypeScript/React 时,组件专属样式扩展是「魔法」(React 用 styled-components / CSS-in-JS 收组件专属属性):

typescript 复制代码
// React 用 styled-components 收组件专属属性
const MyText = styled.span<{ size: number; color: string; bold: boolean }>`
  font-size: ${props => props.size}px;        ← 组件专属属性 fontSize
  color: ${props => props.color};             ← 组件专属属性 color
  font-weight: ${props => props.bold ? 'bold' : 'normal'};
`
function Component() {
  return <MyText size={16} color="#dc2626" bold={true}>样式扩展</MyText>
}
// React 用 styled-components 收组件专属属性+参数,是返值收值魔法

你写鸿蒙 ArkTS 时,@Extend 绑特定组件专属属性扩展------不用返样式对象:

typescript 复制代码
// ArkTS @Extend 绑特定组件(Text)专属属性+支持参数
@Extend(Text)
function MyTextStyle(size: number, color: ResourceColor, isBold: boolean = false) {
  .fontSize(size)                              // ✅ Text 专属属性,@Extend 能写
  .fontColor(color)                            // ✅ Text 专属属性,@Extend 能写
  .fontWeight(isBold ? FontWeight.Bold : FontWeight.Normal)
  .backgroundColor('#f0f0f0')                  // ✅ 通用属性也能写
  .padding(6)
  .borderRadius(4)
}
@Entry
@Component
struct Index {
  build() {
    Column() {
      Text('样式扩展1')
        .MyTextStyle(16, '#dc2626', true)       // ✅ 链式调 @Extend 带参数绑 Text 专属属性
      Text('样式扩展2')
        .MyTextStyle(14, '#2563eb', false)      // ✅ 多调用点复用同 @Extend 不同参数
    }
  }
}
// @Extend 绑特定组件(Text)专属属性+支持参数,不是通用属性集(不收 style 值)

魔法 vs 荬饰器的区别:React 把组件专属样式扩展当 styled-components 收值(返值收值魔法),ArkTS 把 @Extend 当「绑特定组件专属属性荬饰器」(不收值,绑特定组件属性槽+支持参数)。根因不是魔法是特定组件属性槽绑定------@Extend 链式调用不返样式对象,是绑特定组件专属属性的扩展荬饰器。

二、根因:@Extend 的特定组件属性槽绑定扩展机制

鸿蒙 ArkUI 的 @Extend 是特定组件属性槽绑定扩展------编译期把 @Extend 方法绑成特定组件(Text/Button 等)的专属属性槽,链式调用嵌属性槽复用专属属性,支持参数动态调,不是通用属性集,来自三重绑定机制。

机制 1:@Extend 编译期绑特定组件专属属性槽------不是通用属性集

@Extend 荬饰器编译期绑特定组件专属属性槽------把 @Extend 方法编成特定组件的可复用专属属性槽,不是通用属性集:

typescript 复制代码
@Extend(Text)                                    // ✅ 指定绑特定组件 Text
function MyTextStyle(size: number, color: ResourceColor, isBold: boolean = false) {
  .fontSize(size)                                // ✅ Text 专属属性 fontSize(@Extend 能写)
  .fontColor(color)                              // ✅ Text 专属属性 fontColor(@Extend 能写)
  .fontWeight(isBold ? FontWeight.Bold : FontWeight.Normal)
  .backgroundColor('#f0f0f0')                    // ✅ 通用属性也能写
  .padding(6)
  .borderRadius(4)
}
@Entry
@Component
struct Index {
  build() {
    Column() {
      Text('样式扩展')
        .MyTextStyle(16, '#dc2626', true)        // 链式调嵌专属属性槽(绑 Text 属性槽)
    }
  }
}
// 编译期:@Extend(Text) MyTextStyle 绑成 Text 可复用专属属性槽
// 链式调用:.MyTextStyle() 嵌专属属性槽绑 Text(fontSize/fontColor/fontWeight)

编译期绑特定组件属性槽 :@Extend MyTextStyle 荬饰器编译期把方法绑成 Text 的可复用专属属性槽------@Extend(Text) 指定绑 Text 组件,方法里能写 Text 专属属性(fontSize/fontColor/fontWeight)。链式调用 .MyTextStyle(16, '#dc2626', true) 在 Text 上嵌专属属性槽。根因不是通用属性集是特定组件属性槽绑定。

机制 2:支持参数动态调专属属性------按参数变 fontSize/fontColor

@Extend 荬饰器 支持参数动态调专属属性------按参数变 fontSize/fontColor 等专属属性,补 @Styles 不支持参数的边界:

typescript 复制代码
@Extend(Text)
function MyDynamicTextStyle(size: number, color: ResourceColor) {
  .fontSize(size)                                // ✅ 按参数变字号(动态调)
  .fontColor(color)                              // ✅ 按参数变字色(动态调)
  .margin({ top: 4, bottom: 4 })
}
@Entry
@Component
struct Index {
  @State count: number = 0
  build() {
    Column() {
      Text(`动态字号=${10 + this.count} 红色`)
        .MyDynamicTextStyle(10 + this.count, '#dc2626')    // ✅ 按参数变字号(动态调)
      Text(`动态字号=${12 + this.count} 荝色`)
        .MyDynamicTextStyle(12 + this.count, '#2563eb')    // ✅ 多调用点不同参数
      Button('改 count')
        .onClick(() => { this.count++ })          // count 变触发按参数重算字号
    }
  }
}
// @Extend 支持参数:按参数变 fontSize/fontColor 等专属属性(动态调)
// count 变触发按参数重算字号(10 + count / 12 + count),字号动态变

支持参数动态调 :@Extend 支持参数------按参数变 fontSize/fontColor 等专属属性,补 @Styles 不支持参数的边界。MyDynamicTextStyle(10 + this.count, '#dc2626') 按参数变字号(10+count),count 变触发按参数重算字号动态变。根因不是固定属性集是参数动态调专属属性槽绑定。

机制 3:@Extend 必须全局定义------不能写在组件内

@Extend 荬饰器 必须全局定义------加 function 关键字定义在组件外,不能写在组件内(跟 @Styles 可组件内/全局不同):

typescript 复制代码
// ✅ @Extend 必须全局定义(加 function 关键字,写在组件外)
@Extend(Text)
function MyTextStyle(size: number, color: ResourceColor, isBold: boolean = false) {
  .fontSize(size)
  .fontColor(color)
  .fontWeight(isBold ? FontWeight.Bold : FontWeight.Normal)
}

@Entry
@Component
struct Index {
  // ❌ @Extend 不能写在组件内(必须全局)
  // @Extend(Text)
  // MyBadStyle() {
  //   .fontSize(16)    ← @Extend 写组件内报错
  // }
  build() {
    Column() {
      Text('样式扩展')
        .MyTextStyle(16, '#dc2626', true)        // 调全局 @Extend
    }
  }
}
// @Extend 必须全局定义根因:绑特定组件属性槽是全局复用槽,不是组件内复用
// @Styles 可组件内/全局,@Extend 只能全局(绑特定组件全局复用槽)

必须全局定义约束:@Extend 必须全局定义------加 function 关键字定义在组件外,不能写在组件内。根因是 @Extend 绑特定组件属性槽是全局复用槽(所有组件都能调),不是组件内复用槽。@Styles 可组件内/全局(组件内能访问 this/@State),@Extend 只能全局(不能访问 this/@State,但能收参数动态调)。

机制 4:@Extend vs @Styles 边界------专属属性槽 vs 通用属性集

@Extend 绑特定组件专属属性槽+支持参数 ,@Styles 绑通用属性集+不支持参数+不支持专属属性------根因都是属性绑定但绑的属性槽类型不同:

typescript 复制代码
// ✅ @Extend:绑特定组件(Text)专属属性槽+支持参数
@Extend(Text)
function MyTextStyle(size: number, color: ResourceColor, isBold: boolean = false) {
  .fontSize(size)                                // ✅ Text 专属属性,@Extend 能写
  .fontColor(color)                              // ✅ Text 专属属性,@Extend 能写
  .fontWeight(isBold ? FontWeight.Bold : FontWeight.Normal)
  .backgroundColor('#f0f0f0')                    // ✅ 通用属性也能写
  .padding(6)
}

// ✅ @Styles:绑通用属性集+不支持参数+不支持专属属性
@Styles
function MyCommonBox() {
  .width('88%')                                  // ✅ 通用属性,@Styles 能写
  .backgroundColor('#e0e0e0')                    // ✅ 通用属性,@Styles 能写
  .padding(8)
  .borderRadius(6)
  // ❌ .fontSize(16)    ← Text 专属属性,@Styles 报错(@Extend 才能写)
}

@Entry
@Component
struct Index {
  build() {
    Column() {
      Text('专属属性扩展')
        .MyTextStyle(16, '#dc2626', true)        // ✅ @Extend 绑 Text 专属属性+参数
      Column() { Text('通用属性集') }
        .MyCommonBox()                           // ✅ @Styles 绑通用属性集
    }
  }
}
// @Extend 绑特定组件专属属性槽+支持参数(全局定义,不能访问 this)
// @Styles 绑通用属性集+不支持参数(可组件内/全局,组件内能访问 this)

@Extend vs @Styles 边界:@Extend 绑特定组件专属属性槽(fontSize/fontColor/fontWeight 等专属属性)+支持参数+必须全局定义。@Styles 绑通用属性集(width/backgroundColor/padding 等通用属性)+不支持参数+可组件内/全局。根因都是属性绑定但绑的属性槽类型不同------@Extend 绑特定组件专属属性槽(Text/Button 等专属),@Styles 绑通用属性集(所有组件共有)。

三、真机配图:@Extend 专属属性扩展边界------绑 Text 专属属性+支持参数

初始态(@Extend 调用1 显 fontSize=16 红色加粗、调用2 显 fontSize=14 荝色正常、动态调按 count=0 显字号 10/12 均专属属性扩展边界初始值):

点调按钮后(动态字号按 count=1 变了、@Extend 带参数调 Text 专属属性均专属属性扩展边界对比证据齐):

对比证据:点改 count 按钮后动态字号按 count=1 变了(10+1=11、12+1=13,@Extend 带参数动态调字号),调用1 显 fontSize=16 红色加粗、调用2 显 fontSize=14 荝色正常(@Extend 绑 Text 专属属性 fontSize/fontColor/fontWeight)。@Extend 不是通用属性集是绑特定组件专属属性的扩展荬饰器------@Extend 荬饰器绑特定组件(Text)专属属性槽+支持参数,补 @Styles 不支持专属属性/参数的边界。

四、真解法:@Extend 专属属性扩展的三个场景

场景 1:@Extend 绑 Text 专属属性+参数(90% 场景首选,文本样式扩展)

typescript 复制代码
@Extend(Text)
function MyTextStyle(size: number, color: ResourceColor, isBold: boolean = false) {
  .fontSize(size)                                // ✅ 按参数变字号
  .fontColor(color)                              // ✅ 按参数变字色
  .fontWeight(isBold ? FontWeight.Bold : FontWeight.Normal)
  .backgroundColor('#f0f0f0')
  .padding(6)
  .borderRadius(4)
}
@Entry
@Component
struct Index {
  build() {
    Column() {
      Text('样式扩展1')
        .MyTextStyle(16, '#dc2626', true)        // 调用点1 嵌专属属性槽1(字号16/红/加粗)
      Text('样式扩展2')
        .MyTextStyle(14, '#2563eb', false)       // 调用点2 嵌专属属性槽2(字号14/蓝/正常)
    }
  }
}

为哈能跑 :@Extend 绑 Text 专属属性+参数------多个调用点嵌专属属性槽,参数不同显不同文本样式。首选这个,90% 的场景文本样式扩展用 @Extend 绑 Text 专属属性+参数就够。要写「文本样式扩展(fontSize/fontColor/fontWeight 按参数变)」时用这个------不用 @Styles(不支持专属属性)或 inline 样式,@Extend 荬饰器绑 Text 专属属性槽链式调用带参数嵌槽。

场景 2:@Extend 绑 Button 专属属性(按钮样式扩展)

typescript 复制代码
@Extend(Button)
function MyButtonStyle(buttonType: CapsuleStyleType = CapsuleStyleType.CAPSULE) {
  .buttonStyle(buttonType)                       // ✅ Button 专属属性 buttonStyle
  .buttonStyle(CapsuleStyleType.CAPSULE)         // ✅ Button 专属属性
  .width('80%')
  .height(44)
  .backgroundColor('#2563eb')                    // ✅ 通用属性也能写
  .borderRadius(22)
}
@Entry
@Component
struct Index {
  build() {
    Column() {
      Button('胶囊按钮')
        .MyButtonStyle(CapsuleStyleType.CAPSULE)    // 调 @Extend 绑 Button 专属属性
    }
  }
}

为哈能跑:@Extend 绑 Button 专属属性------buttonStyle 等 Button 专属属性塞 @Extend,按参数变按钮样式。要写「按钮样式扩展(buttonStyle 等专属属性按参数变)」时用这个------@Extend 绑 Button 专属属性槽,不用 @Styles(不支持 Button 专属属性 buttonStyle)。

场景 3:@Extend 带状态动态调(按 @State 参数变专属属性)

typescript 复制代码
@Extend(Text)
function MyDynamicTextStyle(size: number, color: ResourceColor) {
  .fontSize(size)                                // ✅ 按参数变字号
  .fontColor(color)                              // ✅ 按参数变字色
  .margin({ top: 4, bottom: 4 })
}
@Entry
@Component
struct Index {
  @State count: number = 0
  build() {
    Column() {
      Text(`动态字号=${10 + this.count} 红色`)
        .MyDynamicTextStyle(10 + this.count, '#dc2626')    // ✅ 按参数变字号(动态调)
      Button('改 count')
        .onClick(() => { this.count++ })          // count 变触发按参数重算字号
    }
  }
}
// @Extend 带状态动态调:按 @State count 参数变专属属性(字号动态变)
// 注意:@Extend 全局定义不能访问 this,但能收参数(调用时传 this.count 进去)

为哈能跑 :@Extend 带状态动态调------调用时传 this.count 进参数,@State 变触发按参数重算专属属性。要写「按状态变专属属性(字号/字色等动态变)」时用这个------@Extend 收参数(调用时传 this.count),@State 变触发按参数重算专属属性重渲。注意:@Extend 全局定义不能访问 this(不像组件内 @Styles 能访问 this),但能收参数绕坑。

五、一句话哲学

@Extend 不是通用属性集,是绑特定组件专属属性的扩展荬饰器。 ArkUI 的 @Extend 荬饰器编译期绑特定组件(Text/Button 等)专属属性槽,链式调用嵌属性槽复用专属属性+支持参数动态调。根因不是通用属性集是特定组件属性槽绑定------@Extend 编译期绑特定组件专属属性槽(fontSize/fontColor 等)+ 支持参数动态调(按参数变专属属性)+ 必须全局定义(绑特定组件全局复用槽)+ @Extend vs @Styles 边界(专属属性槽 vs 通用属性集)。对比 React styled-components 收组件专属属性值,ArkTS @Extend 绑特定组件专属属性槽不收值。

组件设计阶段串讲 :@Builder 绑渲染树节点复用(篇 63,调用点嵌节点实例复用 UI 不返值)→ @BuilderParam 绑渲染树节点参数槽收片段(篇 64,父组件传片段嵌槽复用子组件结构)→ @Styles 绑组件通用属性复用样式集(篇 65,链式调用嵌属性集复用样式不返值)→ @Extend 绑特定组件专属属性槽扩展+支持参数(篇 66,链式调用嵌专属属性槽复用专属属性不返值)------四篇讲清 ArkUI 组件复用哲学:渲染树/属性绑定复用(@Builder 绑节点 / @BuilderParam 绑参数槽 / @Styles 绑通用属性 / @Extend 绑专属属性槽),根因都是绑渲染树/属性不收值不返值。从「UI 片段复用」(@Builder/@BuilderParam)到「样式集复用」(@Styles)到「专属属性扩展」(@Extend)扩展。

@Styles vs @Extend 对比总结:@Styles 绑通用属性集(所有组件共有 width/backgroundColor 等)+ 不支持参数 + 可组件内/全局(组件内能访问 this)。@Extend 绑特定组件专属属性槽(Text 的 fontSize/fontColor 等)+ 支持参数动态调 + 必须全局(不能访问 this 但能收参数)。要写通用样式集复用用 @Styles,要写专属属性扩展+参数动态调用 @Extend。

系列预告:下篇(篇 67,如续)讲 AttributeModifier 动态样式边界(跨文件导出+多态样式+业务逻辑),或系列就此打住。五阶段哲学体系:类型哲学(50-52)→ 作用域哲学(53-55)→ 状态哲学(56-59)→ 渎染哲学(60-62)→ 组件设计(63-66)讲清 ArkTS/ArkUI 进阶哲学。

能力系列回链

能力系列篇 本文进阶点
篇 19 @Builder 用法 @Extend 专属属性扩展边界根因(绑特定组件专属属性槽)
篇 16 组件复用用法 上一篇:@Styles 绑通用属性集根因
篇 13 @State 基础用法 状态哲学:@State 贬值就刷 UI 依赖追踪

真机 demo 完整代码

typescript 复制代码
// 篇 66 demo:@Extend 专属属性扩展 vs @Styles 只收通用属性对比
// 对比:@Extend 绑特定组件专属属性+支持参数合法 vs @Styles 不支持专属属性

// ✅ @Extend 荬饰器:绑特定组件(Text)专属属性,支持参数
@Extend(Text)
function MyTextStyle(size: number, color: ResourceColor, isBold: boolean = false) {
  .fontSize(size)                              // ✅ Text 专属属性,@Extend 能写
  .fontColor(color)                            // ✅ Text 专属属性,@Extend 能写
  .fontWeight(isBold ? FontWeight.Bold : FontWeight.Normal)    // ✅ Text 专属属性
  .backgroundColor('#f0f0f0')                  // ✅ 通用属性也能写
  .padding(6)
  .borderRadius(4)
}

// ✅ @Extend 带参数动态调样式(按参数变 fontSize/fontColor)
@Extend(Text)
function MyDynamicTextStyle(size: number, color: ResourceColor) {
  .fontSize(size)                              // ✅ 按参数变字号
  .fontColor(color)                            // ✅ 按参数变字色
  .margin({ top: 4, bottom: 4 })
}

@Entry
@Component
struct Index {
  @State count: number = 0
  @State log: string = '(未操作)'

  // ✅ @Styles 荬饰器:只写通用属性(对比证据)
  @Styles
  MyCommonBox() {
    .width('88%')
    .backgroundColor('#e0e0e0')
    .padding(8)
    .borderRadius(6)
  }

  build() {
    Column({ space: 12 }) {
      Text('篇 66 配图:@Extend 专属属性扩展边界')
        .fontSize(18).fontWeight(FontWeight.Bold).margin({ top: 20, bottom: 8 })
      Text('@Extend 绑 Text 专属属性+支持参数 vs @Styles 只收通用属性(对比证据)')
        .fontSize(12).fontColor('#888').margin({ bottom: 16 })

      Column({ space: 6 }) {
        Text(`count = ${this.count}`).fontSize(15).fontWeight(FontWeight.Bold)
        Text(`日志:${this.log}`).fontSize(12).fontColor('#333').margin({ top: 4 })
      }
      .width('92%').padding(12).backgroundColor('#f5f5f5').borderRadius(8)

      // ✅ @Extend 调用:绑 Text 专属属性+支持参数
      Text('@Extend 调用1:')
        .fontSize(13)
        .fontColor('#888')
      Text('样式扩展1:fontSize=16 红色加粗')
        .MyTextStyle(16, '#dc2626', true)        // ✅ @Extend 带参数调 Text 专属属性
      Text('样式扩展2:fontSize=14 荝色正常')
        .MyTextStyle(14, '#2563eb', false)       // ✅ 多调用点复用同 @Extend 不同参数

      // ✅ @Extend 带参数动态调样式
      Text('@Extend 带参数动态调:')
        .fontSize(13)
        .fontColor('#888')
        .margin({ top: 8 })
      Text(`动态字号=${10 + this.count} 红色`)
        .MyDynamicTextStyle(10 + this.count, '#dc2626')    // ✅ 按参数变字号(动态调)
      Text(`动态字号=${12 + this.count} 荝色`)
        .MyDynamicTextStyle(12 + this.count, '#2563eb')    // ✅ 多调用点不同参数

      // ✅ @Styles 只收通用属性(对比证据)
      Text('@Styles 只收通用属性:')
        .fontSize(13)
        .fontColor('#888')
        .margin({ top: 8 })
      Column() { Text('通用属性集复用') }
        .MyCommonBox()                          // ✅ @Styles 只绑通用属性,不支持专属属性

      // ❌ @Styles 不支持专属属性(对比证据,注释掉避编译炸)
      // @Styles
      // MyBadTextStyle() {
      //   .fontSize(16)    ← fontSize 是 Text 专属属性,@Styles 报错(@Extend 才能写)
      // }

      Button('改 count(@Extend 带参数动态调样式也刷)')
        .width('92%').height(44).fontSize(14)
        .onClick(() => {
          this.count++                // ✅ count 变 @Extend 带参数动态调样式也刷
          this.log = `count=${this.count}(@Extend 带参数动态调字号也刷)`
        })
    }
    .width('100%').height('100%').alignItems(HorizontalAlign.Center)
  }
}

写鸿蒙 ArkUI 记住 :@Extend 不是通用属性集是绑特定组件专属属性的扩展荬饰器------@Extend 荬饰器编译期绑特定组件(Text/Button 等)专属属性槽,链式调用嵌属性槽复用专属属性+支持参数动态调。根因不是通用属性集是特定组件属性槽绑定------@Extend 编译期绑特定组件专属属性槽(fontSize/fontColor 等)+ 支持参数动态调(按参数变专属属性)+ 必须全局定义(绑特定组件全局复用槽)+ @Extend vs @Styles 边界(专属属性槽 vs 通用属性集)。@Extend 绑 Text 专属属性+参数用文本样式扩展(首选,90% 场景),@Extend 绑 Button 专属属性用按钮样式扩展,@Extend 带状态动态调用按 @State 参数变专属属性(调用时传 this.count)。绑特定组件专属属性槽+支持参数不收值是 ArkUI 组件设计哲学核心!

相关推荐
霸道流氓气质2 小时前
SpringBoot中事务内同步处理 + 事务后异步调用外部系统的通用模式示例
java·spring boot·后端
AskHarries3 小时前
用户埋点怎么设计
后端
极客悟道3 小时前
VS Code + JetTUI 开发 Spring Boot项目,教程来了
后端
Yeauty4 小时前
自建 HLS 第一问:fMP4 还是 TS?用 Rust 在进程内把两种都跑出来
开发语言·后端·rust
Zane19944 小时前
copy 和 deepcopy 到底在拷贝什么?一文讲清赋值、浅拷贝、深拷贝的引用关系
后端·python
zhiSiBuYu05174 小时前
Flask 路由新手入门与实战指南
后端·python·flask
元界metalite4 小时前
禁止 Feign!我们为什么自研 InternalServiceClient
后端
用户125758524364 小时前
进销存后台别急着上线,先重放一次退货请求
人工智能·后端·go
qq_22589174664 小时前
基于Python的城市内涝积涝监测数据可视化分析系统
后端·python·信息可视化·数据分析·django