鸿蒙应用开发 @Builder 使用教程:从入门到精通

这是一个使用鸿蒙技术开发的本地原生记账应用,非常适合大家用来练手。相关源码已上传至 Github,点击此处查看项目。欢迎大家交流、指正,也欢迎提交 PR。

一、引言

在 HarmonyOS 应用开发中,UI 代码的复用性是提升开发效率和代码可维护性的关键。ArkTS 作为鸿蒙的声明式开发语言,提供了 @Builder 装饰器,专门用于构建可复用的 UI 片段。本文将深入浅出地介绍 @Builder 的核心概念、使用场景、参数传递机制以及最佳实践,帮助你彻底掌握这一重要工具。

二、@Builder 装饰器简介

@Builder 是 ArkTS(HarmonyOS 5.0.0+)中用于构建可复用 UI 片段的核心装饰器。它允许开发者将重复的 UI 结构抽象为独立的函数,提升代码的模块化和可维护性。@Builder 装饰的函数称为"自定义构建函数",遵循 ArkUI 的 build() 函数语法规则,用于返回一组 UI 描述。

主要特点

  • 复用性:封装重复的 UI 结构,避免代码冗余。
  • 灵活性:可在组件内部定义(私有)或全局定义(跨组件使用)。
  • 与组件关系@Builder 通常用于 UI 片段的复用,而 @Component 用于定义完整的、带有独立状态和生命周期的自定义组件。

三、基础用法

3.1 组件内 @Builder(私有)

在自定义组件内部定义,仅在该组件内可调用,能够通过 this 访问组件的状态变量。

typescript 复制代码
@Component
struct MyComponent {
  @State count: number = 0;

  @Builder ItemBuilder(text: string) {
    Text(text)
      .padding(10)
      .backgroundColor(0xeeeeee)
  }

  build() {
    Column() {
      this.ItemBuilder("条目1")
      this.ItemBuilder("条目2")
    }
  }
}

3.2 全局 @Builder

在模块级别定义,可在整个应用内调用,但不允许使用 this,因此不适合依赖组件状态的场景。

typescript 复制代码
@Builder
function GlobalBuilder(param: string) {
  Text(param).fontSize(16)
}

@Component
struct MyComponent {
  build() {
    Column() {
      GlobalBuilder("全局构建函数")
    }
  }
}

四、参数传递:按值传递 vs 按引用传递

这是 @Builder 使用中最关键的概念,理解不当会导致 UI 不刷新的问题。

4.1 按值传递(默认)

传递的参数在 @Builder 内部是值的拷贝,外部状态变化不会触发 @Builder 内部的 UI 刷新。

typescript 复制代码
// 按值传递(不响应外部变化)
@Builder
function ValueBuilder(paramA1: string) {
  Row() {
    Text(`Builder - 按值传递: ${paramA1}`)
      .fontSize(20)
  }
}

@Entry
@Component
struct Parent {
  @State label: string = 'Hello';

  build() {
    Column({space: 20}) {
      ValueBuilder(this.label)          // label 变化不会更新

      Divider()

      Text(`label的值:${this.label}`)
        .fontSize(20)

      Button('点击')
        .width('80%')
        .onClick(() => {
        this.label = 'ArkUI'
      })
    }
    .width('100%')
  }
}

运行效果:

4.2 按引用传递(响应外部变化)

使用对象字面量传递参数,当状态变量变化时,@Builder 内部的 UI 会同步刷新。

@Builder 按引用传递的核心规则是:必须且只能传入一个参数,且该参数必须是一个对象字面量。

以下是具体的实现步骤和示例:

  1. 定义一个类或接口,用于封装需要传递的参数。
  2. @Builder 函数中,将参数类型指定为该类或接口。
  3. 在调用 @Builder 时,使用对象字面量 { 属性名: 状态变量 }的形式传入。
typescript 复制代码
// 1. 定义一个类,用于封装参数
class Tmp {
  param: string = '';
}

// 按引用传递(响应外部变化)
// 2. 定义全局 @Builder 函数,参数类型为 Tmp
@Builder
function RefBuilder(tmp: Tmp) {
  Row() {
    Text(`Builder - 按引用传递: ${tmp.param}`)
      .fontSize(20)
  }
}

@Entry
@Component
struct ReferencePassingExample {
  @State label: string = 'Hello';

  build() {
    Column({space: 20}) {
      // 3. 调用时,使用对象字面量形式传入状态变量
      RefBuilder({ param: this.label })

      Divider()

      Text(`label的值:${this.label}`)
        .fontSize(20)

      Button('改变值')
        .width('80%')
        .onClick(() => {
          this.label = 'ArkUI'; // 修改状态变量,会触发 @Builder 内 UI 刷新
        })
    }
    .width('100%')
  }
}

运行效果:

关键点与常见错误:

  1. 必须使用对象字面量:{ param: this.myParam } 这种形式是触发按引用传递的必要条件。
  2. 只能传入一个参数:如果 @Builder 函数需要多个值,必须将它们封装到同一个对象中(如上面的 Tmp 类),不能写成 RefBuilder({ param: this.myParam }, 12)
  3. 不能使用 new 构造对象:RefBuilder(new Tmp(this.myParam)) 这种方式会变成按值传递,UI 不会刷新。
  4. 不能直接传字符串:RefBuilder(this.myParam) 是典型的按值传递,UI 不会刷新。
  5. 禁止修改参数值:在 @Builder 函数内部,不允许修改传入的参数(如 tmp.param = 'new'),否则会抛出运行时错误。
typescript 复制代码
@Builder
function RefBuilder(tmp: Tmp) {
  Row() {
    Text(`Builder - 按引用传递: ${tmp.param}`)
      .fontSize(20)
      .onClick(() => {
        tmp.param = 'new';
      })
  }
}

运行效果:

注意:按值传递时,修改参数值不会报错:

typescript 复制代码
@Builder
function ValueBuilder(paramA1: string) {
  Row() {
    Text(`Builder - 按值传递: ${paramA1}`)
      .fontSize(20)
      .onClick(() => {
        paramA1 = 'World'
        console.log(`修改后的值为:${paramA1}`)
      })
  }
}

运行效果:

五、高级用法与最佳实践

5.1 在 @Builder 中使用循环

结合 ForEach 可以动态生成多个 UI 片段。

typescript 复制代码
@Builder
function ListBuilder(items: string[]) {
  Column() {
    ForEach(items, (item: string) => {
      Text(item)
        .padding(8)
        .width('100%')
    })
  }
}

5.2 嵌套 @Builder

@Builder 函数内部可以调用其他 @Builder 函数,实现更细粒度的复用。

typescript 复制代码
@Builder
function HeaderBuilder(title: string) {
  Text(title)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
}

@Builder
function CardBuilder(title: string, content: string) {
  Column() {
    HeaderBuilder(title)
    Text(content)
      .fontSize(14)
  }
  .padding(16)
  .backgroundColor(0xffffff)
  .borderRadius(8)
}

在组件内 @Builder 中,可以直接使用 this 访问组件的 @State@Prop@Link 等装饰器修饰的变量。

typescript 复制代码
@Component
struct ChildComponent {
  @Prop message: string;

  @Builder MessageBuilder() {
    Text(this.message)
      .fontColor(Color.Blue)
  }

  build() {
    Column() {
      this.MessageBuilder()
    }
  }
}

六、注意事项

  • 参数传递机制 :若 @Builder 需要响应状态变化,务必使用按引用传递(封装为对象)。直接传递基本类型(如 numberstring)会导致 UI 不更新。
  • 作用域选择 :仅在单个组件内复用的 UI 使用组件内 @Builder;无状态、全局复用的 UI 使用全局 @Builder
  • 避免滥用@Builder 适合静态或简单动态 UI 片段;对于具有独立生命周期或复杂逻辑的 UI,应使用 @Component
  • 参数不可变 :在 @Builder 函数内部不要修改参数值(ArkTS 强制约束),如需双向同步,考虑使用 @Link 装饰器。
  • 性能考量:按引用传递可能引起更多 UI 刷新,合理设计参数结构以避免不必要的重绘。

七、总结

@Builder 是 ArkUI 声明式开发范式中提升 UI 复用性的重要工具。正确理解其参数传递机制(按值 vs 按引用)是避免 UI 更新问题的关键。

在开发中,根据场景灵活选择 @Builder@Component,能够构建出既高效又易维护的 HarmonyOS 应用界面。

相关推荐
小满zs4 小时前
关于HBuilderX+ 微信小程序开发工具启动的问题
前端·uni-app
林深见鹿_海蓝见鲸4 小时前
微信小程序反编译完整教程(Windows 新手版)
前端
默_笙4 小时前
😋 我让爬虫终于看到了我的网站,后端同事说"这也行?"(上):SEO 与渲染模式
前端·javascript
2501_919749034 小时前
华为鸿蒙记录怀孕APP—小羊怀孕
华为·harmonyos·鸿蒙
feixing_fx5 小时前
告别枯燥字体:Web 字体加载策略与 font-display 最佳实践
前端·css·前端框架·交互·css3
Liora_Yvonne5 小时前
不懂后端,只会 TypeScript,想独立做完整项目?这套全栈底座就是给前端准备的
前端·后端·全栈
前端Hardy5 小时前
GitHub 爆火!236K+ Star!一套 Skills 让 AI 按工程师方式写代码
前端·后端
烬羽5 小时前
AI Coding 全流程实战:从需求到上线,我用 AI 开发了一个 NPM 包
前端·react.js·ai编程
前进的程序员5 小时前
Codex破局:前端组件秒级生成|提速React/Vue开发,可复用提示词+实战调试经验
前端·vue.js·react.js·codex
前端snow5 小时前
ai agent ---output汇总
前端