🎉 博客主页:【剑九_六千里-CSDN博客】【剑九_六千里-掘金社区】
🎨 上一篇文章:【HarmonyOS第四章:样式操作及渲染页面】
🎠 系列专栏:【HarmonyOS系列】
💖 感谢大家点赞👍收藏⭐评论✍


文章目录
- [1. 组件化抽取](#1. 组件化抽取)
-
- [1.1. 组件拆分及导入导出](#1.1. 组件拆分及导入导出)
- [2. 构建函数抽取](#2. 构建函数抽取)
-
- [2.1. @Builder](#2.1. @Builder)
- [2.2. @Builder传参传递](#2.2. @Builder传参传递)
- [3. 构建函数插槽 @BuilderParam](#3. 构建函数插槽 @BuilderParam)
-
- [3.1. 尾随闭包初始化组件(默认插槽)](#3.1. 尾随闭包初始化组件(默认插槽))
- [3.2. 参数初始化组件(具名插槽)](#3.2. 参数初始化组件(具名插槽))
引言:
在鸿蒙应用开发中,随着应用复杂度的提升,如何有效地组织和管理代码成为了一个重要的课题。
本文将探讨如何通过组件化和构建函数的使用来优化代码结构,提高代码的可读性和可维护性。
我们将从组件化的基础开始,逐步深入到构建函数的高级用法,包括如何使用 @Builder
和 @BuilderParam
来进一步增强组件的灵活性和复用性。
无论你是刚接触鸿蒙应用开发的新手还是有一定经验的开发者,都能从中获得实用的技巧和最佳实践。让我们一起探索这些强大的工具吧!
1. 组件化抽取
1.1. 组件拆分及导入导出
刚开始写鸿蒙代码时,我们的代码都是在一个文件中编写,如下:

- 重复代码较多
- 不利于维护
ts
@Entry
@Component
struct ComponentPage {
build() {
Column() {
Text("header")
.width("100%")
.height(200)
.backgroundColor(Color.Pink)
Text("content")
.width("100%")
.height(200)
.backgroundColor(Color.Red)
Text("footer")
.width("100%")
.height(200)
.backgroundColor(Color.Yellow)
}
}
}
当页面特别复杂时,这样写会导致代码量太大,不利于后续维护,那么可以采用下面这种方式:
创建 components/layout
文件夹:

导出每个组件:
- Header.ets
ts
@Component
export struct Header {
build() {
Text("header")
.width("100%")
.height(200)
.backgroundColor(Color.Pink)
}
}
- Content.ets
ts
@Component
export struct Content {
build() {
Text("content")
.width("100%")
.height(200)
.backgroundColor(Color.Red)
}
}
- Footer.ets
ts
@Component
export struct Footer {
build() {
Text("footer")
.width("100%")
.height(200)
.backgroundColor(Color.Yellow)
}
}
主页面导入并使用:

- 将子组件导入父组件
- 在负组件中直接使用子组件即可
ts
import { Header } from "../components/layout/Header";
import { Content } from "../components/layout/Content";
import { Footer } from "../components/layout/Footer";
@Entry
@Component
struct ComponentPage {
build() {
Column() {
Header()
Content()
Footer()
}
}
}
当然了,在同一个文件中抽取组件也是可行的,如下:

- 在同一文件中通过
@Component
装饰器创建子组件 - 在父组件中引用即可
ts
@Entry
@Component
struct Index {
build() {
Column() {
ListModel()
}
.width("100%")
.height("100%")
}
}
// 同一文件中抽取组件
@Component
struct ListModel {
build() {
Text(`123`)
.fontSize(24)
}
}
2. 构建函数抽取
2.1. @Builder
页面抽取为组件后,组件中有一些相似的内容,又可以抽取为构建函数:

- 将部分UI抽取为公共构建函数
ts
@Component
export struct Header {
// 组件内构建函数:不需要 function 关键字声明
// 通过 this.ListModel() 调用
@Builder ListModel() {
Text(title)
.fontSize(30)
.width("100%")
.height(100)
.backgroundColor(Color.Pink)
}
build() {
Column() {
this.ListModel()
}
}
}
// 全局构建函数:需要 function 关键字声明
// 直接 ListModel() 调用
// @Builder function ListModel() {
// Text("你好")
// .fontSize(30)
// .width("100%")
// .height(100)
// .backgroundColor(Color.Pink)
// }
2.2. @Builder传参传递
1)按值传递

- 标题通过参数传递
- 只传递一个参数
ts
@Component
export struct Header {
// 组件内构建函数:不需要 function 关键字声明
// 通过 this.ListModel() 调用
@Builder ListModel(title: string) {
Text(title)
.fontSize(30)
.width("100%")
.height(100)
.backgroundColor(Color.Pink)
}
build() {
Column() {
this.ListModel("你好")
}
}
}
// 全局构建函数:需要 function 关键字声明
// 直接 ListModel() 调用
// @Builder function ListModel(title: string) {
// Text(title)
// .fontSize(30)
// .width("100%")
// .height(100)
// .backgroundColor(Color.Pink)
// }
2)按引用传递

- 名称总数通过参数传递
- 可传递多个参数
- 点击按钮增加总数
ts
interface IOptions {
name: string;
count: number;
}
@Component
export struct Header {
@State count: number = 1;
// 组件内构建函数:不需要 function 关键字声明
// 通过 this.ListModel() 调用
@Builder ListModel(options: IOptions) {
Text(`${options.name}-------------------${options.count}`)
.width("100%")
.height(20)
.backgroundColor(Color.Pink)
}
build() {
Column() {
this.ListModel({name: "Header", count: this.count})
this.ListModel({name: "Header", count: this.count * 2})
this.ListModel({name: "Header", count: this.count * 4})
Button("增加count").onClick(() => {
this.count++;
})
}
}
}
// 全局构建函数:需要 function 关键字声明
// 直接 ListModel() 调用
// @Builder function ListModel(options: IOptions) {
// Text(`${options.name}-------------------${options.count}`)
// .width("100%")
// .height(20)
// .backgroundColor(Color.Pink)
// }
注意:
- 在使用
@Builder
复用逻辑时,可以支持传递参数,从而实现更灵活的UI渲染。 - 参数可以是状态数据,但建议使用对象的方式进行传递(直接传递,无法实现视图更新)。
- 可以使用
Component
来抽象组件,而@Builder
则可以实现轻量级的UI复用。 - 按引用传递,参数定义类型时,不能使用字面量形式,需要使用
interface
或者type
定义。
3. 构建函数插槽 @BuilderParam
@BuilderParam
是一个装饰器,用于声明任意 UI
描述的一个元素,类似于 slot
占位符。
当开发者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作。
若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。
为解决此问题,ArkUI
引入了 @BuilderParam
装饰器,@BuilderParam
用来装饰指向 @Builder
方法的变量,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的功能。
3.1. 尾随闭包初始化组件(默认插槽)

- 组件后加
{}
书写,称为尾随闭包 - 组件多次使用时,如未传递插槽,需通过
@Builder
声明默认内容
ts
// @BuilderParam
@Entry
@Component
struct Index {
build() {
Column() {
ListModel() {
// 传递默认插槽
Text("默认插槽")
.fontSize(24)
}
// 此处未使用插槽
ListModel()
}
.width("100%")
.height("100%")
}
}
@Component
struct ListModel {
// 使用父组件的尾随闭包{}(@Builder装饰的方法)初始化子组件@BuilderParam
@BuilderParam ListModelFn: () => void = this.ListModelDefaultFn;
// 如果默认插槽未传递,则默认一个内容
@Builder ListModelDefaultFn () {
Text("这是默认内容")
}
build() {
Column() {
Text("尾随闭包")
.fontSize(24)
// 接收默认插槽
this.ListModelFn()
}
}
}
3.2. 参数初始化组件(具名插槽)

@BuilderParam
装饰的方法可以是有参数和无参数的两种形式,需与指向的@Builder
方法类型匹配。@BuilderParam
装饰的方法类型需要和@Builder
方法类型一致。@BuilderParam
装饰的方法必须要有默认值,要不会报错,默认值是一个函数,通过@Builder
装饰
ts
@Entry
@Component
struct Index {
@Builder HeaderContent() {
Text("上部分内容")
.fontColor(Color.White)
.height(40)
.backgroundColor(Color.Blue)
.width("100%")
.textAlign(TextAlign.Center)
}
@Builder FooterContent() {
Text("下部分内容")
.fontColor(Color.White)
.height(40)
.backgroundColor(Color.Blue)
.width("100%")
.textAlign(TextAlign.Center)
}
build() {
Column({space:10}) {
ListModel({ title:"中间内容",
HeaderCon: this.HeaderContent, // 可以传递多个插槽,只传递,不调用
FooterCon: () => { this.FooterContent() } // 也可以才有箭头函数的形式传递
})
}
.width('100%')
.height('100%')
}
}
@Component
struct ListModel {
title:string = "";
@BuilderParam HeaderCon:() => void = this.Default;
@BuilderParam FooterCon:() => void = this.Default;
// 必须要有默认值,要不会报错
@Builder Default () {
Text("")
}
build() {
Column(){
// 组件中调用
this.HeaderCon()
Text(this.title)
.width("100%")
.height(40)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.backgroundColor(Color.Red)
// 组件中调用
this.FooterCon()
}
.width("100%")
}
}