1. 首先, 要介绍一下什么是Builder装饰器
在ArkUI中提供了一种更轻量的UI元素复用机制@Builder,@Builder所装饰的函数遵循build()函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。
使用builder构建函数, 可以建立重复的结构样式编写量
自定义组件内自定义构建函数
定义的语法:
TS
@Builder MyBuilderFunction(){ ... }
使用方法:
TS
this.MyBuilderFunction()
- 允许在自定义组件内定义一个或多个@Builder方法,该方法被认为是该组件的私有、特殊类型的成员函数。
- 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。
- 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
全局自定义构建函数
定义的语法:
TS
@Builder function MyGlobalBuilderFunction(){ ... }
使用方法:
TS
MyGlobalBuilderFunction()
- 全局的自定义构建函数可以被整个应用获取,不允许使用this和bind方法。
- 如果不涉及组件状态变化,建议使用全局的自定义构建方法。
2. 构建需求场景
1. 同一个全局构建函数, 如何使其触发不同的点击事件?
2. 全局构建函数, 如何更改外部组件的变量?
3.解决问题
显然, 在正常的全局构建函数中, 是没有办法做到以上两点的. 但是如果对于一个需要导出重复使用的组件来说, 在不使用@component装饰器的情况下, 有没有办法做到以上两点呢? 答案当然是可以的
3.1 同一个全局构建函数, 如何使其触发不同的点击事件?
对于这个问题, 其实很简单. 因为在builder构建函数内, 是可以传递参数的.
除了比较常见的简单类型的参数外, 其实还可以传递复杂类型的参数, 包括对象,或者函数等等.
在这里用得到方法就是回调函数. 我们可以把一个函数作为参数传入进去, 并且在点击时调用这个函数. 这样我们就可以在同一个构建函数上显示不同的点击功能
TS
@Entry
@Component
struct chiqingsan {
build() {
Column({ space: 20 }) {
mybutton(
() => {
// 配置不同的点击逻辑
AlertDialog.show({
message: "我是第一个按钮~"
})
}
)
mybutton(
() => {
AlertDialog.show({
message: "我是第二个按钮~"
})
})
mybutton(
() => {
AlertDialog.show({
message: "我是第三个按钮~"
})
})
}.width("100%").height("100%")
}
}
@Builder
export function mybutton(fn?: () => void) {
Button("按钮")
.onClick(() => {
fn()
})
}
3.2 全局构建函数, 如何更改外部组件的变量?
在官网的 开发文档 中, 介绍了两种构建函数传值的方式, 一种是按值传递, 一种是引用传递. 这里我们用到的是引用传递.
但是需要注意的是, 在编辑器中使用开发文档中提供的语法是会直接报错的. 也就是说, 在本篇文章写下的当前(2024.04.21), 官网的文档是落后于编辑器版本的.
其正确的写法是:
TS
// 1. 首先定义对象的结构类型
interface mytype {
num: number
}
// 使用$$符来完成引用传值
@Builder
export function mybutton($$: mytype) {
Button($$.num.toString())
}
这样我们就完成了这个需求的第一步, 当外部的值改变时, 构建函数所引用的值也会同步的重新渲染.
那我们是不是可以在内部直接对值进行修改呢? 答案是不行的, 当我们在构建函数内部对引用传递过来的值做改变时, 里立马报错.
typescript
// 1. 首先定义对象的结构类型
interface mytype {
num: number
}
// 使用$$符来完成引用传值
@Builder
export function mybutton($$: mytype) {
Button("按钮")
.onClick(() => {
$$.num++
})
}
显然, 直接修改是不允许的. 这是鸿蒙ArkUI对开发所作出的限制.
那就没办法了吗? 不, 是有办法的. 既然我们不能直接去修改传过来的值, 那么间接的修改呢?结合第一个问题提到的回调函数, 我们对代码做一些修改
TS
@Entry
@Component
struct chiqingsan {
@State num1: number = 0
build() {
Column({ space: 20 }) {
mybutton({
num: this.num1, fn: () => {
this.num1++
}
})
}.width("100%").height("100%")
}
}
// 1. 首先定义对象的结构类型
interface mytype {
num: number
fn: () => void
}
// 使用$$符来完成引用传值
@Builder
export function mybutton($$: mytype) {
Button($$.num.toString())
.onClick(() => {
$$.fn()
})
}
这样, 我们做到了在全局构建函数内部去影响到了外部的值. 实现的过程是:
- 创建好需要引用传值过来的数据结构, 使用$$引用传递来传值
- 在构建函数内部, 拿到引用传递过来的值, 这样就可以在外面值改变的时候, 构建函数内同步的进行更新
- 在点击事件内, 调用传递过来的回调函数
- 因为回调函数在外层, 所以可以直接对外部的变量进行修改. 这样就避免了在构建函数内部直接修改引用传递过来的值所造成的报错
- 外部的值发生改变, 引发构造函数内的值同步改变. 视图重新渲染
到此, 我们就完成了在全局构建函数, 去更改外部组件的变量,并且视图同步刷新
4.总结
其实, 在ArkUI的开发中, 官方是跟推荐使用@component组件去完成以上两个操作的.
以上案例, 只是我在使用全局builder的时候的一些奇思妙想, 并且对如何去实现的一些研究与记录. 前端框架茫茫多,还得慢慢学习啊