HarmonyOS 自定义组件:@Builder/@Component 复用艺术


一、引言
在大型应用中,UI 结构往往存在大量重复。如果每个页面都从零编写 UI,不仅代码冗余,而且难以维护。HarmonyOS ArkUI 提供了强大的组件复用机制,包括 @Component 自定义组件、@Builder 构建函数、@BuilderParam 插槽、@Styles 样式复用、@Extend 扩展样式等。
本文将以一个圆润可爱的紫粉风格页面为主线,深入讲解 ArkUI 组件复用的各种机制,通过大量代码示例帮助读者掌握"一次编写、多处复用"的精髓。
二、组件复用的层次
ArkUI 的组件复用可以从多个层次进行:
| 复用层次 | 机制 | 说明 |
|---|---|---|
| 结构复用 | @Component | 封装完整组件,含状态和逻辑 |
| 片段复用 | @Builder | 复用一段 UI 结构 |
| 插槽复用 | @BuilderParam | 自定义组件的内容插槽 |
| 样式复用 | @Styles | 复用通用样式 |
| 扩展复用 | @Extend | 扩展系统组件样式 |
| 性能复用 | @Reusable | 列表滚动时的组件复用 |
三、@Component 自定义组件
3.1 基本概念
@Component 装饰的结构体就是一个自定义组件。它由两部分组成:状态数据(@State、@Prop 等)和 build 方法(定义 UI 结构)。
@Component
struct MyComponent {
// 状态数据
@Prop title: string = '';
@State count: number = 0;
// UI 结构
build() {
Column() {
Text(this.title)
Text(`${this.count}`)
}
}
}
3.2 组件的生命周期
自定义组件有完整的生命周期方法:
@Component
struct LifecycleDemo {
aboutToAppear(): void {
// 组件即将显示,适合初始化数据
console.info('组件即将出现');
}
aboutToDisappear(): void {
// 组件即将销毁,适合释放资源
console.info('组件即将消失');
}
onPageShow(): void {
// 页面显示时触发
}
onPageHide(): void {
// 页面隐藏时触发
}
}
四、@Builder 构建函数
@Builder 是 ArkUI 中最灵活的 UI 复用机制,它允许我们将一段 UI 结构封装为函数,在多个地方调用。
4.1 基本用法
@Builder
MyTitle(title: string) {
Text(title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
}
4.2 在 build 中调用
build() {
Column() {
this.MyTitle('标题一')
this.MyTitle('标题二')
this.MyTitle('标题三')
}
}
4.3 传参的两种方式
方式一:按值传递
@Builder
Card(title: string, color: string) {
Column() {
Text(title)
}
.backgroundColor(color)
}
方式二:按引用传递(推荐)
@Builder
Card(item: { title: string, color: string }) {
Column() {
Text(item.title)
}
.backgroundColor(item.color)
}
按引用传递时,当传入对象的属性变化时,@Builder 内的 UI 会同步刷新,这是按值传递不具备的能力。
五、实战代码:紫粉可爱风自定义组件页面
下面我们实现一个演示组件复用的页面,页面采用圆润可爱的紫粉渐变风格。
5.1 定义数据结构
interface PropRow {
prop: string;
value: string;
}
代码说明:
PropRow 接口描述组件复用机制对照表中的一行数据,包含机制名称和说明文字。
5.2 定义可复用组件
// 自定义组件:@Builder 复用块
@Component
struct PillCard {
@Prop title: string = '';
@Prop color: string = '#A55EEA';
build() {
Column({ space: 6 }) {
Text(this.title)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
Text('@Builder 复用卡片')
.fontSize(10)
.fontColor('#FFFFFFCC')
}
.width(150)
.height(90)
.justifyContent(FlexAlign.Center)
.backgroundColor(this.color)
.borderRadius(20)
.shadow({ radius: 10, color: '#33A55EEA', offsetY: 4 })
}
}
代码说明:
PillCard 是一个可复用的卡片组件:
-
@Prop 参数:
title和color两个属性,由父组件传入。color控制卡片背景色,让同一个组件可以呈现不同颜色。 -
@Prop 默认值:
@Prop title: string = ''设置了默认值,即使父组件不传参也不会报错。 -
UI 结构:卡片包含标题和说明文字,居中排列。
-
圆润风格:
.borderRadius(20)大圆角、.shadow柔和阴影,体现"圆润可爱"的设计风格。 -
复用价值:这个组件可以在任何页面中使用,只需传入不同的
title和color,就能生成不同样式的卡片,无需重复编写 UI 代码。
5.3 定义主页面
@Entry
@Component
struct CustomComponentPage {
@State props: PropRow[] = [
{ prop: '@Component', value: '声明一个自定义组件' },
{ prop: '@Builder', value: '复用一段 UI 结构' },
{ prop: '@BuilderParam', value: '自定义组件插槽' },
{ prop: '@Styles', value: '复用通用样式' },
{ prop: '@Extend', value: '扩展系统组件样式' },
{ prop: '@Reusable', value: '组件复用优化性能' }
];
@State colors: string[] = ['#A55EEA', '#FF6B9D', '#7ED6DF', '#FFEAA7', '#82CCDD', '#F8A5C2'];
代码说明:
@State props:组件复用机制对照表数据。@State colors:一组颜色值,用于动态生成不同颜色的卡片,演示组件的参数化复用。
5.4 构建 UI
build() {
Scroll() {
Column({ space: 16 }) {
// 顶部紫粉渐变
Column() {
Text('COMPONENT')
.fontSize(12)
.fontColor('#FFE0F0')
.letterSpacing(4)
Text('自定义组件')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.margin({ top: 6 })
Text('@Builder / @Component 复用之美')
.fontSize(12)
.fontColor('#FFE0F0')
.margin({ top: 6 })
}
.width('100%')
.padding({ top: 48, bottom: 30 })
.linearGradient({
angle: 160,
colors: [['#A55EEA', 0], ['#FF6B9D', 1]]
})
代码说明:
顶部标题区使用紫粉渐变(从紫色 #A55EEA 到粉色 #FF6B9D),angle: 160 表示渐变方向。粉色文字配合白色主标题,形成可爱风格。
// Flex 弹性换行区
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceAround, alignItems: ItemAlign.Center }) {
ForEach(this.colors, (c: string, i: number) => {
PillCard({ title: `卡片 ${i + 1}`, color: c })
.margin({ bottom: 12 })
})
}
.width('100%')
代码说明:
这是组件复用的核心演示:
-
Flex 弹性布局:
Flex({ wrap: FlexWrap.Wrap })设置自动换行,justifyContent: FlexAlign.SpaceAround让卡片均匀分布。 -
循环创建组件:
ForEach遍历colors数组,每次迭代创建同一个PillCard组件,但传入不同的color。 -
一次定义、多次复用:
PillCard组件只定义了一次,却生成了 6 张不同颜色的卡片。这就是组件复用的价值——一份代码,多种形态。 -
关键理解:组件复用的核心是"参数化"。通过将可变部分(颜色、标题、内容)抽象为参数,同一个组件就能适应多种场景。
// 圆形彩色按钮组
Row({ space: 18 }) {
ForEach(this.colors, (c: string, i: number) => {
Button(`${i + 1}`)
.width(52)
.height(52)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.backgroundColor(c)
.borderRadius(26)
.shadow({ radius: 8, color: '#33A55EEA', offsetY: 3 })
.onClick(() => {
this.props = this.props.slice(0, 6);
})
})
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding({ top: 8, bottom: 8 })
代码说明:
圆形彩色按钮组:
- 使用
ForEach循环创建 6 个圆形按钮,每个按钮颜色不同。 .borderRadius(26)(52 的一半)将按钮变成正圆。- 这种"同构异色"的设计风格与卡片区呼应,体现了页面的整体设计语言。
// 属性表格:圆角标签行
Column({ space: 8 }) {
Text('组件复用机制速查')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#7B2FBE')
.alignSelf(ItemAlign.Start)
ForEach(this.props, (row: PropRow) => {
Row({ space: 12 }) {
Text(row.prop)
.fontSize(12)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.backgroundColor('#A55EEA')
.borderRadius(12)
Text(row.value)
.fontSize(12)
.fontColor('#555555')
.layoutWeight(1)
}
.width('100%')
.padding(10)
.backgroundColor('#FFFFFF')
.borderRadius(14)
.shadow({ radius: 4, color: '#11000000', offsetY: 2 })
})
}
.width('100%')
.padding(14)
.backgroundColor('#F7EFFC')
.borderRadius(20)
代码说明:
组件复用机制速查表采用"圆角标签行"样式,区别于传统的表格:
- 每行左侧是紫色圆角标签(机制名称),右侧是说明文字。
- 每行是一个白色圆角卡片,带轻微阴影,形成"标签列表"效果。
- 这种设计既有表格的信息展示功能,又比传统表格更活泼可爱,符合页面整体风格。
六、@BuilderParam 插槽机制
@BuilderParam 允许自定义组件接收外部传入的 UI 内容,类似 Vue 的插槽(slot)概念:
@Component
struct CardContainer {
@BuilderParam content: () => void;
build() {
Column() {
Text('卡片容器')
.fontSize(16)
.fontWeight(FontWeight.Bold)
// 渲染外部传入的内容
this.content()
}
.padding(16)
.backgroundColor('#F5F5F5')
.borderRadius(12)
}
}
// 使用
@Component
struct Parent {
@Builder
customContent() {
Row() {
Text('这是自定义内容')
Button('按钮')
}
}
build() {
CardContainer({ content: this.customContent })
}
}
代码说明:
@BuilderParam content: () => void声明一个插槽,类型是"无参无返回值的函数"。- 在组件内部通过
this.content()调用,渲染外部传入的 UI。 - 父组件通过
CardContainer({ content: this.customContent })传入自定义的@Builder函数。 - 这种机制让组件更加灵活,同一个容器组件可以承载不同的内容。
七、@Styles 样式复用
@Styles 用于封装一组通用样式,避免重复书写相同的样式链:
// 全局样式
@Styles
function globalCardStyle() {
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 8, color: '#22000000', offsetY: 4 })
.padding(16)
}
// 组件内样式
@Component
struct Demo {
@Styles
cardStyle() {
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
build() {
Column() {
Text('卡片一').cardStyle()
Text('卡片二').cardStyle()
}
}
}
代码说明:
@Styles可以定义在组件外部(全局)或组件内部(局部)。- 全局
@Styles函数名使用驼峰命名,组件内@Styles方法名首字母小写。 - 通过
.cardStyle()链式调用复用样式。 @Styles只能包含通用属性(颜色、尺寸、边框等),不能包含事件回调。
八、@Extend 扩展组件样式
@Extend 用于扩展特定组件的样式,可以包含该组件特有的属性和事件:
@Extend(Text)
function priceText() {
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6348')
.onClick(() => {
console.info('价格被点击');
})
}
// 使用
Text('¥99').priceText()
代码说明:
@Extend(Text)表示扩展 Text 组件的样式。- 与
@Styles不同,@Extend可以包含事件回调(如onClick)。 @Extend只能用于特定组件,而@Styles可以用于所有组件。
九、@Reusable 组件复用优化
在长列表滚动场景中,频繁创建和销毁组件会导致性能问题。@Reusable 允许组件在滚动时被复用:
@Reusable
@Component
struct ListItemView {
@State item: string = '';
aboutToReuse(params: Record<string, Object>): void {
// 组件被复用时更新数据
this.item = params.item as string;
}
build() {
Row() {
Text(this.item)
}
.height(60)
}
}
代码说明:
@Reusable标记组件可复用。aboutToReuse方法在组件被复用时调用,用于更新数据。- 在
List中使用ListItem包裹@Reusable组件,滚动时框架会自动复用,大幅提升性能。
十、组件复用最佳实践
10.1 组件拆分原则
- 单一职责:一个组件只做一件事。
- 参数化设计:将可变部分抽象为参数。
- 合理粒度:避免组件过大或过小。
10.2 状态管理边界
自定义组件内部使用 @State 管理私有状态,通过 @Prop/@Link 与父组件通信,保持数据流清晰。
10.3 命名规范
- 组件名使用大驼峰命名(如
PillCard)。 @Builder方法名使用小驼峰(如cardStyle)。- 全局
@Styles函数使用大驼峰。
10.4 复用与性能
- 列表场景使用
@Reusable提升滚动性能。 - 避免在
@Builder中创建过多嵌套层级。 - 合理使用
ForEach的键生成器优化列表复用。
十一、常见问题
11.1 @Builder 传参不刷新
原因:按值传递的参数变化不会触发 @Builder 内 UI 刷新。
解决:改用按引用传递(对象参数)。
11.2 @BuilderParam 调用报错
原因:没有正确传入 @Builder 函数,或函数签名不匹配。
解决:确保传入的函数是 @Builder 装饰的,且签名一致。
11.3 @Styles 与 @Extend 混用报错
原因:@Styles 不能包含事件,@Extend 只能用于特定组件。
解决:根据需求选择合适的机制。
十二、总结
本文深入讲解了 HarmonyOS 自定义组件的各种复用机制,并通过一个紫粉可爱风格的页面进行了实战演示。
核心要点回顾:
@Component封装完整组件,含状态和逻辑。@Builder复用一段 UI 结构,支持按值和按引用传参。@BuilderParam实现插槽机制,让组件内容可定制。@Styles复用通用样式,@Extend扩展特定组件样式。@Reusable提升列表滚动性能。- 组件复用的核心是"参数化设计",一份代码多种形态。
掌握组件复用是提升开发效率和代码质量的关键。下一篇我们将深入讲解 HarmonyOS 动画与转场技术。
更多推荐

所有评论(0)