本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

鸿蒙开发中,当创建自定义组件时,经常会遇到这样的需求:需要为组件添加特定功能(如页面跳转、点击事件等),但如果直接在组件内嵌入事件方法,会导致所有该组件的实例都具备相同的功能,缺乏灵活性。为了解决这个问题,ArkUI引入了@BuilderParam装饰器。

@BuilderParam用于装饰指向@Builder方法的变量,允许在初始化自定义组件时,通过不同的方式对@BuilderParam装饰的自定义构建函数进行传参赋值,从而实现组件功能的灵活定制。

一、@BuilderParam装饰器

@BuilderParam装饰器用于接收@Builder装饰的函数,将其作为参数传递给自定义组件,使得父组件可以动态决定子组件的UI结构或行为。

核心作用

  • 解耦组件功能:将特定功能从组件内部剥离,由父组件按需注入

  • 提高复用性:同一个组件可以通过不同的@BuilderParam实现不同的展示效果

  • 灵活定制:支持多种传参方式(参数修改、尾随闭包、箭头函数等)

支持版本

版本 支持范围
API version 7 开始支持
API version 9+ 支持在ArkTS卡片中使用
API version 11+ 支持在元服务中使用

二、装饰器使用

2.1 基本语法

@BuilderParam variableName: () => void = initialBuilderFunction;

2.2 初始化方式

@BuilderParam装饰的方法只能被自定义构建函数(@Builder装饰的方法)初始化

方式一:本地初始化

使用所属自定义组件的自定义构建函数或全局自定义构建函数,在本地初始化@BuilderParam。

@Builder
function overBuilder() {
    Text('全局Builder')
}

@Component
struct Child {
    @Builder
    doNothingBuilder() {
        Text('本地Builder')
    }

    // 使用自定义组件的自定义构建函数初始化
    @BuilderParam customBuilderParam: () => void = this.doNothingBuilder;

    // 使用全局自定义构建函数初始化
    @BuilderParam customOverBuilderParam: () => void = overBuilder;

    build() {
        Column() {
            this.customBuilderParam()
            this.customOverBuilderParam()
        }
    }
}
方式二:父组件初始化

使用父组件的自定义构建函数初始化子组件的@BuilderParam。

@Component
struct Child {
    @Builder
    customBuilder() {
        Text('默认Builder')
    }
    
    @BuilderParam customBuilderParam: () => void = this.customBuilder;
    
    build() {
        Column() {
            this.customBuilderParam()  // 调用注入的Builder
        }
    }
}

@Entry
@Component
struct Parent {
    @Builder
    componentBuilder() {
        Text('Parent builder')
    }
    
    build() {
        Column() {
            // 将父组件的Builder注入子组件
            Child({ customBuilderParam: this.componentBuilder })
        }
    }
}

2.3 this指向问题

在传递@Builder时,需要注意this的指向,它会影响Builder内部访问的变量值。

@Component
struct Child {
    label: string = 'Child';
    
    @Builder
    customBuilder() { }
    
    @Builder
    customChangeThisBuilder() { }
    
    @BuilderParam customBuilderParam: () => void = this.customBuilder;
    @BuilderParam customChangeThisBuilderParam: () => void = this.customChangeThisBuilder;
    
    build() {
        Column() {
            this.customBuilderParam()
            this.customChangeThisBuilderParam()
        }
    }
}

@Entry
@Component
struct Parent {
    label: string = 'Parent';
    
    @Builder
    componentBuilder() {
        Text(`${this.label}`)  // 这里的this取决于调用时的上下文
    }
    
    build() {
        Column() {
            // 直接调用,this指向Parent,显示"Parent"
            this.componentBuilder()
            
            Child({
                // 直接传递Builder,this会指向子组件Child,显示"Child"
                customBuilderParam: this.componentBuilder,
                
                // 使用箭头函数包装,this指向父组件Parent,显示"Parent"
                customChangeThisBuilderParam: (): void => {
                    this.componentBuilder()
                }
            })
        }
    }
}

说明

  • customBuilderParam:显示"Child"(this指向子组件)

  • customChangeThisBuilderParam:显示"Parent"(箭头函数保持父组件this)

三、限制条件

3.1 初始化值必须为@Builder

@BuilderParam装饰的变量只能通过@Builder函数进行初始化,不能使用普通变量或状态变量。

3.2 与@Require联用的限制

当@Require装饰器和@BuilderParam装饰器一起使用时,必须从外部初始化@BuilderParam装饰器。

3.3 尾随闭包的限制

在自定义组件尾随闭包的场景下:

  • 子组件有且仅有一个@BuilderParam用来接收此尾随闭包

  • 此@BuilderParam装饰的方法不能有参数

  • 此场景下自定义组件不支持通用属性

// 只有一个无参@BuilderParam
@Component
struct CustomContainer {
    @BuilderParam closer: () => void;  // 只能有一个,且无参
    
    build() {
        Column() {
            this.closer()
        }
    }
}

// 使用尾随闭包
CustomContainer() {
    // 尾随闭包内容
    Text('这是尾随闭包内容')
}

四、使用场景

4.1 场景一:参数初始化组件

@BuilderParam装饰的方法可以是有参数或无参数的形式,但必须与指向的@Builder方法类型匹配。

class Tmp {
    public label: string = '';
}

@Builder
function overBuilder($$: Tmp) {
    Text($$.label)
        .width('100%')
        .height(50)
        .backgroundColor(Color.Green)
}

@Component
struct Child {
    label: string = 'Child';
    
    @Builder
    customBuilder() {
        Text('默认Builder')
    }
    
    // 无参数类型
    @BuilderParam customBuilderParam: () => void = this.customBuilder;
    
    // 有参数类型
    @BuilderParam customOverBuilderParam: ($$: Tmp) => void = overBuilder;
    
    build() {
        Column() {
            this.customBuilderParam()
            this.customOverBuilderParam({ label: 'global Builder label' })
        }
    }
}

@Entry
@Component
struct Parent {
    label: string = 'Parent';
    
    @Builder
    componentBuilder() {
        Text(`${this.label}`)
    }
    
    build() {
        Column() {
            this.componentBuilder()
            Child({ 
                customBuilderParam: this.componentBuilder,
                customOverBuilderParam: overBuilder 
            })
        }
    }
}

4.2 场景二:尾随闭包初始化组件

尾随闭包是一种特殊的初始化方式,允许在创建组件时,通过紧跟一个大括号{}来传递Builder内容。

示例1:基础尾随闭包用法
@Component
struct CustomContainer {
    @Prop header: string = '';
    
    @Builder
    closerBuilder() { }
    
    // 使用父组件的尾随闭包初始化
    @BuilderParam closer: () => void = this.closerBuilder;
    
    build() {
        Column() {
            Text(this.header).fontSize(30)
            this.closer()  // 调用尾随闭包传入的内容
        }
    }
}

@Builder
function specificParam(label1: string, label2: string) {
    Column() {
        Text(label1).fontSize(30)
        Text(label2).fontSize(30)
    }
}

@Entry
@Component
struct CustomContainerUser {
    @State text: string = 'header';
    
    build() {
        Column() {
            // 使用尾随闭包传递Builder内容
            CustomContainer({ header: this.text }) {
                Column() {
                    specificParam('testA', 'testB')
                }
                .backgroundColor(Color.Yellow)
                .onClick(() => {
                    this.text = 'changeHeader';
                })
            }
        }
    }
}
示例2:与@ComponentV2结合使用
@ComponentV2
struct ChildPage {
    @Require @Param message: string = '';
    
    @Builder
    customBuilder() { }
    
    @BuilderParam customBuilderParam: () => void = this.customBuilder;
    
    build() {
        Column() {
            Text(this.message).fontSize(30).fontWeight(FontWeight.Bold)
            this.customBuilderParam()
        }
    }
}

const builderValue: string = 'Hello World';

@Builder
function overBuilder() {
    Row() {
        Text(`Global Builder: ${builderValue}`)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
    }
}

@Entry
@ComponentV2
struct ParentPage {
    @Local label: string = 'Parent Page';
    
    @Builder
    componentBuilder() {
        Row() {
            Text(`Local Builder: ${this.label}`)
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
        }
    }
    
    build() {
        Column() {
            // 使用局部@Builder的尾随闭包
            ChildPage({ message: this.label }) {
                Column() {
                    this.componentBuilder();
                }
            }
            
            Line().width('100%').height(10).backgroundColor('#000000').margin(10)
            
            // 使用全局@Builder的尾随闭包
            ChildPage({ message: this.label }) {
                Column() {
                    overBuilder();
                }
            }
        }
    }
}

4.3 场景三:使用全局和局部@Builder初始化@BuilderParam

通过不同的传递方式,控制Builder内部的this指向,实现不同的展示效果。

@Component
struct ChildPage {
    label: string = 'Child Page';
    
    @Builder
    customBuilder() { }
    
    @BuilderParam customBuilderParam: () => void = this.customBuilder;
    @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
    
    build() {
        Column() {
            this.customBuilderParam()
            this.customChangeThisBuilderParam()
        }
    }
}

const builderValue: string = 'Hello World';

@Builder
function overBuilder() {
    Row() {
        Text(`Global Builder: ${builderValue}`)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
    }
}

@Entry
@Component
struct ParentPage {
    label: string = 'Parent Page';
    
    @Builder
    componentBuilder() {
        Row() {
            Text(`Local Builder: ${this.label}`)
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
        }
    }
    
    build() {
        Column() {
            // 直接调用,显示"Parent Page"
            this.componentBuilder()
            
            ChildPage({
                // 直接传递,this指向子组件,显示"Child Page"
                customBuilderParam: this.componentBuilder,
                
                // 箭头函数包装,this指向父组件,显示"Parent Page"
                customChangeThisBuilderParam: (): void => {
                    this.componentBuilder()
                }
            })
            
            Line().width('100%').height(10).backgroundColor('#000000').margin(10)
            
            // 调用全局Builder,显示"Hello World"
            overBuilder()
            
            ChildPage({
                // 传递全局Builder,都显示"Hello World"
                customBuilderParam: overBuilder,
                customChangeThisBuilderParam: overBuilder
            })
        }
    }
}

4.5 场景五:在@ComponentV2中使用@BuilderParam

在V2版本的组件模型中,@BuilderParam同样可以配合全局或局部@Builder使用。

@ComponentV2
struct ChildPage {
    @Param label: string = 'Child Page';
    
    @Builder
    customBuilder() { }
    
    @BuilderParam customBuilderParam: () => void = this.customBuilder;
    @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
    
    build() {
        Column() {
            this.customBuilderParam()
            this.customChangeThisBuilderParam()
        }
    }
}

const builderValue: string = 'Hello World';

@Builder
function overBuilder() {
    Row() {
        Text(`Global Builder: ${builderValue}`)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
    }
}

@Entry
@ComponentV2
struct ParentPage {
    @Local label: string = 'Parent Page';
    
    @Builder
    componentBuilder() {
        Row() {
            Text(`Local Builder: ${this.label}`)
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
        }
    }
    
    build() {
        Column() {
            // 直接调用,显示"Parent Page"
            this.componentBuilder()
            
            ChildPage({
                customBuilderParam: this.componentBuilder,           // 显示"Child Page"
                customChangeThisBuilderParam: (): void => {         // 显示"Parent Page"
                    this.componentBuilder()
                }
            })
            
            Line().width('100%').height(5).backgroundColor('#000000').margin(10)
            
            // 全局Builder
            overBuilder()  // 显示"Hello World"
            
            ChildPage({
                customBuilderParam: overBuilder,           // 都显示"Hello World"
                customChangeThisBuilderParam: overBuilder
            })
        }
    }
}

注意事项

  1. 必须用@Builder初始化:普通变量不行

  2. 类型必须匹配:参数签名要一致

  3. 注意this指向:箭头函数可以保持父组件上下文

  4. 尾随闭包特殊:只能有一个且无参

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐