@Extend 扩展函数 --- 告别重复样式代码

概述
在鸿蒙 UI 开发中,重复的样式代码是降低可维护性的主要因素之一。当一个项目中有 20 个 Text 组件需要使用相同的字体颜色、字号和对齐方式时,最原始的做法是逐个编写样式属性,这不仅效率低下,而且当设计规范调整时需要逐个修改。ArkTS 的 @Extend 装饰器允许为系统组件(如 Text、Image、Button 等)扩展自定义方法,将一组样式封装为可复用的函数调用。与 @Styles 不同,@Extend 可以接收参数,因此更加灵活。MoneyTrack 项目使用 @Extend 统一管理全局样式,有效减少了代码重复,并确保了跨页面的 UI 风格一致性。
核心知识点
1. @Extend 的基本用法
@Extend 用于为系统组件添加自定义方法,只能在 .ets 文件的顶层定义(不能在组件内部)。定义后,可以在任何组件中直接以链式调用的方式使用:
typescript
@Extend(Text)
function filterTextStyle() {
.fontColor($r('app.color.font_color_level1'))
.fontWeight(FontWeight.Medium)
.fontSize($r('app.string.font_size_16'));
}
// 使用
Text(this.vm.dataProcessing.dateLabel).filterTextStyle();
2. @Extend 与 @Styles、@Builder 的详细对比
三种 UI 复用方式各有适用场景,下表从多个维度进行对比:
| 对比维度 | @Extend | @Styles | @Builder |
|---|---|---|---|
| 适用对象 | 指定系统组件(如 Text、Button) | 通用组件(任何组件均可调用) | 任意 UI 片段 |
| 参数支持 | ✅ 支持参数 | ❌ 不支持参数 | ✅ 支持参数 |
| 定义位置 | 仅文件顶层 | 文件顶层或组件内部 | 文件顶层或组件内部 |
| 调用方式 | 链式方法调用(.xxxStyle()) | 链式方法调用(.xxxStyle()) | 函数调用(this.xxxBuilder()) |
| 是否可以 export | ✅ 可以 | ❌ 不可以 | ✅ 可以 |
| 引用状态变量 | ❌ 不能直接引用,需通过参数传入 | ❌ 不能直接引用 | ✅ 可以通过闭包捕获 |
| 复用范围 | 当前文件或通过 export 跨文件 | 当前文件或组件内部 | 当前文件或通过 export 跨模块 |
| 典型场景 | 统一 Text/Button/Image 样式 | 组件内部的通用样式复用 | 页面区域拆分、插槽注入 |
选择策略:单一组件的样式复用用 @Extend;不限组件的通用样式用 @Styles;复杂 UI 片段复用用 @Builder。
3. 带参数的 @Extend 高级用法
@Extend 可以接受参数,实现高度灵活的样式控制。CommonButton.ets 展示了多个带参数的 @Extend 协同工作的模式:
typescript
@Extend(Button)
function commonButtonStyle(isFullWidth: boolean) {
.fontSize($r('app.string.font_size_16'))
.fontWeight(500)
.width(isFullWidth ? '100%' : 'auto')
.padding({ left: $r('app.string.space_l'), right: $r('app.string.space_l') })
.height(40);
}
@Extend(Button)
function primaryButtonStyle(themeColor: ResourceColor) {
.backgroundColor(themeColor)
.fontColor($r('app.color.icon_color_level1_invert'));
}
// 组合使用
Button(this.title)
.commonButtonStyle(this.fullWidth)
.primaryButtonStyle(this.themeColor)
通过链式调用组合多个 @Extend 方法,可以实现基础样式 + 变体样式的灵活组合,类似于 CSS 中的复合 class 模式。
4. @Extend 中引用状态变量的限制
@Extend 方法在顶层定义,无法直接访问组件内部的 @Local、@Param 等状态变量。如果 @Extend 的行为需要依赖组件状态,必须通过参数显式传入:
typescript
// ❌ 错误:无法访问组件内部状态
@Extend(Text)
function errorStyle() {
.fontColor(this.isError ? Color.Red : Color.Black); // 编译错误
}
// ✅ 正确:通过参数传入
@Extend(Text)
function statusStyle(isError: boolean) {
.fontColor(isError ? Color.Red : Color.Black);
}
这是一个重要的设计约束------@Extend 应当是纯样式函数,其行为完全由参数决定,不依赖外部上下文。
5. @Extend 与全局样式管理
在大型应用中,建议将 @Extend 方法集中组织在专门的样式文件中,形成全局样式方法库。MoneyTrack 采用了按组件类型分类的组织方式:
typescript
// styles/TextStyles.ets
@Extend(Text)
export function titleStyle() { ... }
@Extend(Text)
export function bodyStyle() { ... }
// styles/ImageStyles.ets
@Extend(Image)
export function iconStyle() { ... }
// styles/ButtonStyles.ets
@Extend(Button)
export function primaryStyle(themeColor: ResourceColor) { ... }
通过 export 关键字导出后,其他文件可以 import 使用,实现了样式方法的跨文件复用。这种方式比传统的复制粘贴样式代码更易于维护------当设计规范更新时,只需修改一处定义。
项目案例
d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\features\statistics\src\main\ets\views\StatisticsView.ets 中 @Extend(Text).filterTextStyle() 统一定义筛选栏文本样式。d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\features\mine\src\main\ets\views\MineView.ets 中 commonIconStyle 和 commonLabelStyle 统一定义个人中心页面的图标和标签样式。d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\commons\commonlib\src\main\ets\components\CommonButton.ets 中多个 @Extend(Button) 方法展示了参数化样式和链式组合的最佳实践。
总结
@Extend 是 ArkTS 中最灵活的样式复用工具。它通过为系统组件扩展自定义方法,将一组样式属性封装为可复用的函数调用;其参数化能力使其能够适应不同场景的样式变体。与 @Styles 和 @Builder 相比,@Extend 在"单一组件的参数化样式复用"场景中具有独特优势。在 MoneyTrack 中,@Extend 被用于统一定义按钮、图标、标签等高频组件的样式,配合 export 导出机制形成了全局样式方法库。最佳实践是:按组件类型组织 @Extend 方法(如 TextStyles、ButtonStyles),采用"基础样式 + 变体参数"的命名规范(如 primaryButtonStyle、secondaryButtonStyle),并通过参数而非上下文依赖实现行为定制。
参考文档
- @Extend 装饰器 API 参考
- @Extend 与 @Styles 的选择策略
- 全局样式复用最佳实践