本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

在HarmonyOS应用开发中,@Styles装饰器解决了通用样式的复用问题,但针对特定组件的私有样式复用,需要更强大的工具。@Extend装饰器应运而生,它在@Styles的基础上提供了更细粒度的组件样式扩展能力。

一、@Extend装饰器

@Extend装饰器是HarmonyOS提供的一种组件样式扩展机制,允许为指定组件封装私有属性、私有事件和自定义方法,实现针对特定组件的样式复用。

与@Styles的核心区别

特性 @Styles @Extend
适用范围 所有组件的通用属性/事件 指定组件的通用+私有属性/事件
参数支持 不支持 支持
定义位置 组件内或全局 仅全局
调用方式 直接调用方法名 组件实例调用

支持版本

版本 支持范围
API version 9 开始支持
API version 9+ 支持在ArkTS卡片中使用
API version 11+ 支持在元服务中使用

二、装饰器使用说明

2.1 语法结构

@Extend(UIComponentName) function functionName { ... }
  • UIComponentName:指定要扩展的组件类型(如Text、Button、Column等)

  • functionName:自定义的样式方法名

2.2 核心特性

特性一:支持组件私有属性
// @Extend(Text)可以支持Text的私有属性fontColor
@Extend(Text)
function fancy() {
    .fontColor(Color.Red)  // Text组件的私有属性
}

// @Extend(Button)可以支持Button的私有属性
@Extend(Button)
function roundButton() {
    .borderRadius(20)      // Button组件的私有属性
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
}
特性二:支持方法嵌套调用
// 先定义基础样式
@Extend(Text)
function fancy() {
    .fontColor(Color.Red)
    .fontStyle(FontStyle.Italic)
}

// 在另一个@Extend中调用已定义的样式
@Extend(Text)
function superFancyText(size: number) {
    .fontSize(size)
    .fancy()  // 调用预定义的fancy样式
}
特性三:支持参数传递

@Extend最重要的特性之一就是支持参数,这是与@Styles最大的区别。

@Extend(Text)
function fancy(fontSize: number) {
    .fontColor(Color.Red)
    .fontSize(fontSize)
}

@Entry
@Component
struct FancyUse {
    build() {
        Row({ space: 10 }) {
            Text('Fancy')
                .fancy(16)  // 传入16
            Text('Fancy')
                .fancy(24)  // 传入24
        }
    }
}
特性四:支持事件句柄作为参数
@Extend(Text)
function makeMeClick(onClick: () => void) {
    .backgroundColor(Color.Blue)
    .onClick(onClick)  // 传入的事件处理函数
}

@Entry
@Component
struct FancyUse {
    @State label: string = 'Hello World';
    
    onClickHandler() {
        this.label = 'Hello ArkUI';
    }
    
    build() {
        Row({ space: 10 }) {
            Text(`${this.label}`)
                .makeMeClick(() => {
                    this.onClickHandler();
                })
        }
    }
}
特性五:支持状态变量
@Extend(Text)
function fancy(fontSize: number) {
    .fontColor(Color.Blue)
    .fontSize(fontSize)
}

@Entry
@Component
struct FancyUse {
    @State fontSizeValue: number = 20;
    
    build() {
        Column({ space: 10 }) {
            Text('Fancy')
                .fancy(this.fontSizeValue)  // 传入状态变量
                .onClick(() => {
                    this.fontSizeValue = 30;  // 改变状态变量
                })
        }
        .width('100%')
    }
}

效果

  • 初始字体大小为20

  • 点击后字体大小变为30

  • UI能够正常刷新渲染

三、与@Styles混用限制

3.1 禁止混用规则

@Extend中不能调用@Styles定义的方法。

// 错误示例:@Extend不能调用@Styles
@Styles
function fancy() {
    .backgroundColor(Color.Red)
}

@Extend(Text)
function superFancyText(size: number) {
    .fontSize(size)
    .fancy()  // 编译错误!不能调用@Styles方法
}

3.2 正确做法

如果需要组合使用,应该都在@Extend中定义:

// 正确做法:统一使用@Extend
@Extend(Text)
function baseFancy() {
    .backgroundColor(Color.Red)
}

@Extend(Text)
function superFancyText(size: number) {
    .fontSize(size)
    .baseFancy()  // 调用@Extend方法(允许)
}

四、限制条件

4.1 仅支持全局定义

@Extend必须在全局定义,不支持在组件内部定义。

// 错误示例:组件内定义
@Entry
@Component
struct FancyUse {
    // 编译错误!@Extend仅支持在全局定义
    @Extend(Text) function fancy(fontSize: number) {
        .fontSize(fontSize)
    }
    
    build() {
        Text('Fancy')
            .fancy(16)
    }
}

// 正确示例:全局定义
@Extend(Text)
function fancy(fontSize: number) {
    .fontSize(fontSize)
}

@Entry
@Component
struct FancyUse {
    build() {
        Text('Fancy')
            .fancy(16)
    }
}

4.2 不支持导出

// 不支持export
export @Extend(Text) function fancy() {  // 错误
    .fontColor(Color.Red)
}

// 仅限当前文件使用
@Extend(Text)
function fancy() {
    .fontColor(Color.Red)
}

提示:如果需要实现样式导出功能,推荐使用AttributeModifier

五、使用示例

有3个Text组件,每个都需要设置相同的fontStyle(斜体),但不同的fontWeight(字重)和backgroundColor(背景色)。

未使用@Extend的写法

@Entry
@Component
struct FancyUse {
    @State label: string = 'Hello World';
    
    build() {
        Row({ space: 10 }) {
            Text(`${this.label}`)
                .fontStyle(FontStyle.Italic)
                .fontWeight(500)
                .backgroundColor(Color.Yellow)
            
            Text(`${this.label}`)
                .fontStyle(FontStyle.Italic)
                .fontWeight(600)
                .backgroundColor(Color.Pink)
            
            Text(`${this.label}`)
                .fontStyle(FontStyle.Italic)
                .fontWeight(700)
                .backgroundColor(Color.Orange)
        }
        .margin('20%')
    }
}

问题:代码重复,fontStyle设置重复了3次,维护困难。

5.3 使用@Extend优化

第一步:定义扩展样式

@Extend(Text)
function fancyText(weightValue: number, color: Color) {
    .fontStyle(FontStyle.Italic)  // 公共样式提取一次
    .fontWeight(weightValue)       // 参数化的样式
    .backgroundColor(color)        // 参数化的样式
}

第二步:应用扩展样式

@Entry
@Component
struct FancyUse {
    @State label: string = 'Hello World';
    
    build() {
        Row({ space: 10 }) {
            Text(`${this.label}`)
                .fancyText(500, Color.Yellow)
            
            Text(`${this.label}`)
                .fancyText(600, Color.Pink)
            
            Text(`${this.label}`)
                .fancyText(700, Color.Orange)
        }
        .margin('20%')
    }
}

六、@Extend vs @Styles 

对比维度 @Styles @Extend
适用组件 所有组件 指定组件
支持属性 仅通用属性/事件 通用属性 + 组件私有属性/事件
参数支持 不支持 支持
定义位置 组件内/全局 仅全局
嵌套调用 不支持调用@Extend 支持调用其他@Extend
状态变量 支持(组件内) 支持
事件句柄 支持 支持
导出能力 不支持 不支持
使用复杂度
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐