在实际开发中,我们经常会遇到这样的需求:当使用一个封装好的自定义组件时,希望在其内部自由插入任意子组件,以增强其灵活性和复用性。为了在不改动组件源码的前提下支持“向内部插槽插入子组件”,ArkUI 提供了 @BuilderParam 属性修饰符。下面从实战出发,完整介绍如何为自定义组件添加插槽能力。

1. 问题背景:直接嵌套失败

假设我们有这样一个最简单的自定义组件 CustomWidget,仅返回一个带背景色的 Column 容器:

@Component
struct CustomWidget {
  build() {
    Column() {
      // 原始内部结构
    }
    .width("100%")
    .height(100)
    .backgroundColor("#aabbcc")
  }
}

如果像使用普通容器那样,直接给它添加子组件:

@Entry @Component
struct CustomWidgetTest {
  build() {
    Column() {
      CustomWidget() {
        Text("测试子组件")
      }
    }
    .width("100%")
    .height("100%")
  }
}

编译时就会报错:

ETS:ERROR … In the trailing lambda case, 'CustomWidget' must have one and only one
property decorated with @BuilderParam, and its @BuilderParam expects no parameter.

这说明:若想让 CustomWidget 接受嵌套构造子组件,必须在其内部显式声明一个标记为 @BuilderParam 的属性。

2. @BuilderParam 修饰符简介

common.d.ts 中,@BuilderParam 定义如下:

/**
 * Defining BuilderParam PropertyDecorator
 * @since 7
 */
declare const BuilderParam: PropertyDecorator;
  • 作用:将组件的某个属性标记为“插槽”占位,该属性的类型必须为一个无参返回 JSX.Element(或组件容器)的函数签名:() => {}
  • 约束:每个组件只能有且只有一个 @BuilderParam 属性,且不能带参数。

3. 在组件内部声明插槽

修改 CustomWidget,新增一个 @BuilderParam child 属性,并在 build() 方法中多次调用它来渲染插入内容:

@Component
struct CustomWidget {

  @BuilderParam
  child: () => {};  // 插槽属性,占位用

  build() {
    Column() {
      Text("原始子组件 1")
        .fontSize(20)

      this.child()   // 在此渲染外部插入的子组件

      Text("原始子组件 2")
        .fontSize(20)

      this.child()   // 同一插槽可重复调用
    }
    .width("100%")
    .height(100)
    .backgroundColor("#aabbcc")
  }
}

这样,CustomWidget 就在两处预留了“插槽”位置,外部传入的子组件会在这两处同时渲染。

4. 在使用端插入子组件

在父组件中,就可以像使用系统容器那样,将任意组件写在 CustomWidget() 后的花括号里:

@Entry @Component
struct CustomWidgetTest {
  build() {
    Column() {
      CustomWidget() {  
        Text("插入子组件 AAA")
          .fontSize(18)
          .backgroundColor(Color.Pink)

        Text("插入子组件 BBB")
          .fontSize(18)
          .backgroundColor(Color.PeachPuff)
      }
    }
    .width("100%")
    .height("100%")
  }
}

运行效果:

  • CustomWidget 在其两处 this.child() 位置,都会同时渲染 “AAA” 与 “BBB” 两个 Text
  • 如果只想渲染一次插入内容,也可以在组件内部只调用一次 this.child()

5. 小结

  • 当自定义组件需要开放“容器式插槽”以供外部灵活注入子组件时,务必在组件内部声明一个带有 @BuilderParam 的属性,并在 build() 中调用它;
  • 使用端只需在组件构造后紧跟花括号,像普通容器一样编写子节点即可;
  • 这种插槽机制无需修改组件外的 API,只需在组件内部做最小范围的改动,大大提升了自定义组件的复用性与扩展性。
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐