本文代码案例基于Api13。
@Builder装饰器,可以把build函数中的组件代码,单独的抽取出来,虽然简化了build函数,实现了组件之间的复用,但是代码还是在整个UI视图内,如下案例所示:
@Entry
@Component
struct Index {
@Builder
TextView(text: string) {
Text(text)
}
build() {
Column() {
this.TextView("测试数据1")
this.TextView("测试数据2")
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
只能说,在同一个UI视图中,实现了组件复用,简化了代码,但是,如果有很多个页面共用一个组件呢?还好,@Builder装饰器支持全局定义,这个在以往的文章中有过概述,可以把共用的组件抽取到一个全局文件中。
@Builder
export function TextView(text: string) {
Text(text)
}
在需要的地方直接调用即可。
import { TextView } from './Views'
@Entry
@Component
struct Index {
build() {
Column() {
TextView("测试数据1")
TextView("测试数据2")
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
哎,回过头来,我们发现,@Builder装饰器可局部可全局,已经满足了需求了啊,还要wrapBuilder干什么?先别着急,我们先看下这几个场景:
1、有一个自定义Dialog弹窗,需要接收从外部传递UI视图过来,请问,如何把组件传递过去?
2、有一个封装的网络库,其中有一个功能是请求加载Loading,由于每个项目的Loading不同,这个Loading组件视图需要单独传递,如何传递?
怎么能把外部定义的UI组件,传递给需要的地方呢?这些需要的地方可不是struct修饰的,大家想想看,能直接传递吗?
再看一个案例,虽然说,我们可以定义全局的@Builder装饰器,然而当我组合成数组后,却无法进行调用,如下代码:
import { TextView } from './Views';
let builderArr: Function[] = [TextView]
@Entry
@Component
struct Index {
build() {
Column() {
ForEach(builderArr, (item: Function) => {
item()
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
直接报错:
'item()' does not comply with the UI component syntax. <ArkTSCheck>
为了解决以上的问题,鸿蒙引入wrapBuilder作为全局@Builder封装函数,返回WrappedBuilder对象,以实现全局@Builder可以进行赋值和传递。
wrapBuilder是一个模板函数,返回一个WrappedBuilder对象。
declare function wrapBuilder< Args extends Object[]>(builder: (...args: Args) => void): WrappedBuilder;
声明变量
let builderVar: WrappedBuilder<[string, number]> = wrapBuilder(MyBuilder)
简单案例:
import { TextView } from './Views'
@Entry
@Component
struct Index {
textView: WrappedBuilder<[string]> = wrapBuilder(TextView)
build() {
Column() {
this.textView.builder("测试数据")
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
当然了,也可以直接在定义组件的地方进行声明变量。
@Builder
function TextView(test: string) {
Text(test)
}
export let textView: WrappedBuilder<[string]> = wrapBuilder(TextView)
调用:
import { textView } from './Views'
@Entry
@Component
struct Index {
build() {
Column() {
textView.builder("测试数据")
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
其实在上边的案例中,已经是传递参数的案例,遵循着,接收什么类型就传递什么类型。
比如接收的是string类型。
let textView: WrappedBuilder<[string]> = wrapBuilder(TextView)
比如接收两个参数,一个是string,一个是number。
@Builder
function TextView(test: string,num:number) {
Text(test)
}
export let textView: WrappedBuilder<[string,number]> = wrapBuilder(TextView)
如果传递参数过多,可以通过对象,数组,集合等形式传递,下面举一个引用类型传递:
export class TestBean {
testData?: string = "测试数据"
}
import { TestBean } from "./TestBean"
@Builder
function TextView(test: TestBean) {
Text(test.testData)
}
export let textView: WrappedBuilder<[TestBean]> = wrapBuilder(TextView)
import { TestBean } from './TestBean';
import { textView } from './Views'
@Entry
@Component
struct Index {
@State test: TestBean = new TestBean();
build() {
Column() {
textView.builder({ testData: this.test.testData })
Button("点击").onClick(() => {
this.test.testData = "改变数据"
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
首先第一点,在同一个UI组件内,同一个wrapBuilder只能初始化一次,第二点就是WrappedBuilder对象的builder属性方法只能在struct内部使用。
如何接收一个外部传来的UI组件呢,很是简单,只需要接收一个WrappedBuilder<[Object]>即可,简单案例如下:
export class ViewUtils {
view?: WrappedBuilder<[Object]>
setView(view: WrappedBuilder<[Object]>) {
this.view = view
}
getView(): WrappedBuilder<[Object]> | undefined {
return this.view
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。