鸿蒙开发:熟知@BuilderParam装饰器
前言 本文代码案例基于Api13。 在实际的开发中,我们经常会遇到自定义组件的情况,比如通用的列表组件,选项卡组件等等,由于使用方的样式不一,子组件是动态变化的,针对这一情况,就不得不让使用方把子组件视图传递过来,如何来接收这个UI视图,这就是@BuilderParam装饰器的作用。 简单案例 简单封装一个通用的List组件,由于每个列表的数据和UI布局都是不一样的,那么这两块就需要暴露给使用方,代码如下: /** * AUTHOR:AbnerMing * DATE:2025/2/13 * INTRODUCE:通用的列表组件 * */ @Component export struct ListView { dataList?: Object[] //数据源 @BuilderParam itemLayout: (item: Object, index: number) => void //子视图 build() { List() { ForEach(this.dataList, (item: Object, index: number) => { ListItem() { this.itemLayout(item, index) } }) } } } 以上的自定义List组件,我们就可以用在任何页面,只需要传递数据和子视图即可。 @Entry @Component struct Index { @Builder itemLayout(item: Object, index: number) { Text(item.toString()) } build() { Column() { ListView({ dataList: [0, 1, 2, 3, 4, 5, 6], itemLayout: this.itemLayout }) } .height('100%') .width('100%') } } @BuilderParam装饰器让UI组件赋予了动态的变化,可以根据自身需要实现不同的效果,避免了实例增加了相同的功能,从而实现更高程度的组件复用和代码解耦。 使用方式 @BuilderParam装饰器,常见于自定义组件,暴露给使用方进行调用,用来承接@Builder装饰器修饰的函数,使用方式很简单,格式如下: @BuilderParam test: () => void 如果接收参数,直接在括号中添加即可。 除了正常的由调用者传递UI组件之外,我们也可以初始化一个默认的视图,直接在后面等于即可,这样在未传递的话就会加载默认的视图。 @Builder testView() { } @BuilderParam test: () => void = this.testView() this指向问题 如下所示,我们自定义了一个组件,定义了两个@BuilderParam,用于接收传递的UI视图: @Component export struct TestView { testContent: string = "TestView" @BuilderParam layout: () => void @BuilderParam layout2: () => void build() { Column() { if (this.layout != undefined) { this.layout() } if (this.layout2 != undefined) { this.layout2() } } } } 我们通过三种方式,分别调用@Builder修饰的函数。 @Entry @Component struct Index { testContent: string = "Index" @Builder testView() { Text(`${this.testContent}`) .fontSize(20) .margin({ top: 20 }) .fontWeight(FontWeight.Bold) } build() { Column() { this.testView() TestView({ layout: this.testView }) TestView({ layout2: () => { this.testView() } }) } .height('100%') .width('100%') } } 运行之后,效果如下: 我们可以看到,直接调用@Builder修饰的函数,也就是this.testView()这行代码,this指向的是当前的Index类,其值也是取的Index中值。 当我们以参数的形式,传递给@BuilderParam时,也就是TestView({ layout: this.testView })这行代码,可以发现,其this并不是指的是Index类,而是自定义组件TestView,取的是TestView中所定义的值。 当我们针对自定义组件,换种方式使用时,也就是如下方式使用: TestView({ layout2: () => { this.testView() } }) 可以发现,this又切换为了当前类Index,这是因为箭头函数的this指向的是宿主对象,所以其值取的是Index类中的。 所以在有@BuilderParam传递UI视图时,一定要注意this的指向问题,这也是为什么很多同学遇到在@Builder修饰的函数中为什么不刷新数据的问题,其原因就是this指向不对。 数据更新 更新@BuilderParam装饰器,本意就是更新对应的@Builder修饰的函数,这个在《鸿蒙开发:了解@Builder装饰器》一篇中已经重点做了讲解,这里再重新概述一下。 简单自定义一个组件,使用 @BuilderParam装饰器,对外暴露一个UI视图。 @Component export struct TestView { @BuilderParam layout: () => void build() { Column() { if (this.layout != undefined) { this.layout() } } } } 上面已经讲述过this指向问题了,如果数据在本页面内,那么一定要使用箭头函数来调用@Builder修饰的函数,才能实现数据的更新。 @Entry @Component struct Index { @State testContent: string = "测试数据一" @Builder textView() { Text(this.testContent) .fontSize(20) .fontWeight(FontWeight.Bold) } build() { Column() { TestView({ layout: () => { this.textView() } }) Button("点击") .onClick(() => { this.testContent = "测试数据二" }) } .height('100%') .width('100%') } } 相关总结 在声明@BuilderParam的时候,如果未有默认值,那么在不传递的情况下,会发生异常崩溃,为了解决这一问题,有两种方案,方案一,主动设置默认值: @Builder testView() { } @BuilderParam test: () => void = this.testView() 方案二,在调用的地方进行非空校验: @BuilderParam test: () => void build() { if (this.test != undefined) { this.test() } } @BuilderParam用于接收@Builder定义的函数,私有和全局都可以。 定义全局的@Builder。 @Builder export function TextView() { Text("测试数据一") .fontSize(20) .fontWeight(FontWeight.Bold) } 调用 @Entry @Component struct Index { build() { Column() { TestView({ layout: TextView }) } .height('100%') .width('100%') } }

前言
本文代码案例基于Api13。
在实际的开发中,我们经常会遇到自定义组件的情况,比如通用的列表组件,选项卡组件等等,由于使用方的样式不一,子组件是动态变化的,针对这一情况,就不得不让使用方把子组件视图传递过来,如何来接收这个UI视图,这就是@BuilderParam装饰器的作用。
简单案例
简单封装一个通用的List组件,由于每个列表的数据和UI布局都是不一样的,那么这两块就需要暴露给使用方,代码如下:
/**
* AUTHOR:AbnerMing
* DATE:2025/2/13
* INTRODUCE:通用的列表组件
* */
@Component
export struct ListView {
dataList?: Object[] //数据源
@BuilderParam itemLayout: (item: Object, index: number) => void //子视图
build() {
List() {
ForEach(this.dataList, (item: Object, index: number) => {
ListItem() {
this.itemLayout(item, index)
}
})
}
}
}
以上的自定义List组件,我们就可以用在任何页面,只需要传递数据和子视图即可。
@Entry
@Component
struct Index {
@Builder
itemLayout(item: Object, index: number) {
Text(item.toString())
}
build() {
Column() {
ListView({
dataList: [0, 1, 2, 3, 4, 5, 6],
itemLayout: this.itemLayout
})
}
.height('100%')
.width('100%')
}
}
@BuilderParam装饰器让UI组件赋予了动态的变化,可以根据自身需要实现不同的效果,避免了实例增加了相同的功能,从而实现更高程度的组件复用和代码解耦。
使用方式
@BuilderParam装饰器,常见于自定义组件,暴露给使用方进行调用,用来承接@Builder装饰器修饰的函数,使用方式很简单,格式如下:
@BuilderParam test: () => void
如果接收参数,直接在括号中添加即可。
除了正常的由调用者传递UI组件之外,我们也可以初始化一个默认的视图,直接在后面等于即可,这样在未传递的话就会加载默认的视图。
@Builder
testView() {
}
@BuilderParam test: () => void = this.testView()
this指向问题
如下所示,我们自定义了一个组件,定义了两个@BuilderParam,用于接收传递的UI视图:
@Component
export struct TestView {
testContent: string = "TestView"
@BuilderParam layout: () => void
@BuilderParam layout2: () => void
build() {
Column() {
if (this.layout != undefined) {
this.layout()
}
if (this.layout2 != undefined) {
this.layout2()
}
}
}
}
我们通过三种方式,分别调用@Builder修饰的函数。
@Entry
@Component
struct Index {
testContent: string = "Index"
@Builder
testView() {
Text(`${this.testContent}`)
.fontSize(20)
.margin({ top: 20 })
.fontWeight(FontWeight.Bold)
}
build() {
Column() {
this.testView()
TestView({ layout: this.testView })
TestView({
layout2: () => {
this.testView()
}
})
}
.height('100%')
.width('100%')
}
}
运行之后,效果如下:
我们可以看到,直接调用@Builder修饰的函数,也就是this.testView()这行代码,this指向的是当前的Index类,其值也是取的Index中值。
当我们以参数的形式,传递给@BuilderParam时,也就是TestView({ layout: this.testView })这行代码,可以发现,其this并不是指的是Index类,而是自定义组件TestView,取的是TestView中所定义的值。
当我们针对自定义组件,换种方式使用时,也就是如下方式使用:
TestView({
layout2: () => {
this.testView()
}
})
可以发现,this又切换为了当前类Index,这是因为箭头函数的this指向的是宿主对象,所以其值取的是Index类中的。
所以在有@BuilderParam传递UI视图时,一定要注意this的指向问题,这也是为什么很多同学遇到在@Builder修饰的函数中为什么不刷新数据的问题,其原因就是this指向不对。
数据更新
更新@BuilderParam装饰器,本意就是更新对应的@Builder修饰的函数,这个在《鸿蒙开发:了解@Builder装饰器》一篇中已经重点做了讲解,这里再重新概述一下。
简单自定义一个组件,使用 @BuilderParam装饰器,对外暴露一个UI视图。
@Component
export struct TestView {
@BuilderParam layout: () => void
build() {
Column() {
if (this.layout != undefined) {
this.layout()
}
}
}
}
上面已经讲述过this指向问题了,如果数据在本页面内,那么一定要使用箭头函数来调用@Builder修饰的函数,才能实现数据的更新。
@Entry
@Component
struct Index {
@State testContent: string = "测试数据一"
@Builder
textView() {
Text(this.testContent)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
build() {
Column() {
TestView({
layout: () => {
this.textView()
}
})
Button("点击")
.onClick(() => {
this.testContent = "测试数据二"
})
}
.height('100%')
.width('100%')
}
}
相关总结
在声明@BuilderParam的时候,如果未有默认值,那么在不传递的情况下,会发生异常崩溃,为了解决这一问题,有两种方案,方案一,主动设置默认值:
@Builder
testView() {
}
@BuilderParam test: () => void = this.testView()
方案二,在调用的地方进行非空校验:
@BuilderParam test: () => void
build() {
if (this.test != undefined) {
this.test()
}
}
@BuilderParam用于接收@Builder定义的函数,私有和全局都可以。
定义全局的@Builder。
@Builder
export function TextView() {
Text("测试数据一")
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
调用
@Entry
@Component
struct Index {
build() {
Column() {
TestView({ layout: TextView })
}
.height('100%')
.width('100%')
}
}