
前言在 ArkUI 开发中代码复用是保持项目可维护性的关键。HarmonyOS 提供了Builder和BuilderParam两个装饰器用于实现UI 构建逻辑的复用和组件插槽化。本文将以“海风日记“中的多个实战案例深入讲解 Builder 和 BuilderParam 的用法、机制和最佳实践包括自定义 Tab 构建器、可复用的操作菜单项、组件插槽等核心模式。一、Builder 装饰器概述1.1 什么是 BuilderBuilder是 ArkUI 提供的自定义构建函数装饰器用于封装一段 UI 构建逻辑使其可以被多次复用。与 Component 不同Builder不创建新的组件实例而是内联展开构建逻辑。// 定义一个 Builder 构建函数 Builder tabBuilder(index: number, label: string, icon: Resource) { Column({ space: 2 }) { SymbolGlyph(icon).fontSize(22) Text(label).fontSize(10) } }1.2 Builder 与 Component 的对比对比维度BuilderComponent本质构建函数内联展开独立的组件类实例化开销无有组件实例化开销参数传递函数参数Prop / Link / State状态管理无独立状态有独立 State复用场景局部复用全局复用生命周期无有完整生命周期1.3 Builder 的两种定义方式方式一在组件内定义通过 this 访问Component export struct Index { State currentIndex: number 0 Builder tabBuilder(index: number, label: string, icon: Resource) { Column({ space: 2 }) { SymbolGlyph(icon) .fontSize(22) .fontColor([this.currentIndex index ? COLOR_TAB_ACTIVE : COLOR_TAB_INACTIVE]) Text(label) .fontSize(10) .fontColor(this.currentIndex index ? COLOR_TAB_ACTIVE : COLOR_TAB_INACTIVE) } } }方式二全局定义独立于组件// 全局构建函数不依赖组件状态 Builder function globalTabBuilder(label: string, icon: Resource) { Column({ space: 2 }) { SymbolGlyph(icon).fontSize(22) Text(label).fontSize(10) } }二、Builder 实战Tab 导航栏构建器2.1 统一 Tab 构建器“海风日记“中的tabBuilder是最典型的 Builder 应用Builder tabBuilder(index: number, label: string, icon: Resource) { Column({ space: 2 }) { SymbolGlyph(icon) .fontSize(22) .fontColor([this.currentIndex index ? COLOR_TAB_ACTIVE : COLOR_TAB_INACTIVE]) Text(label) .fontSize(10) .fontColor(this.currentIndex index ? COLOR_TAB_ACTIVE : COLOR_TAB_INACTIVE) .fontWeight(this.currentIndex index ? FontWeight.Bold : FontWeight.Normal) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) }2.2 Builder 的参数传递机制Builder支持传递任意类型的参数参数类型示例说明基本类型index: number数字、字符串、布尔值Resourceicon: Resource通过$r()获取的资源引用函数类型onTap: () void回调函数自定义类型item: MenuItemData自定义接口/类2.3 在 Tabs 中调用 Builder调用 Builder 时使用this关键字引用// 在 tabBar 中调用 Builder TabContent() { HomeTab() } .tabBar(this.tabBuilder(0, 首页, $r(sys.symbol.house))) TabContent() { CalendarTab() } .tabBar(this.tabBuilder(1, 日历, $r(sys.symbol.calendar))) TabContent() { ReviewTab() } .tabBar(this.tabBuilder(3, 回顾, $r(sys.symbol.clock))) TabContent() { MineTab() } .tabBar(this.tabBuilder(4, 我的, $r(sys.symbol.person)))三、Builder 高级用法3.1 条件构建在 Builder 内部可以使用条件渲染Builder actionItem(icon: string, iconBg: string, label: string, isDanger: boolean) { Row({ space: 14 }) { Text(icon).fontSize(18) .width(36).height(36) .textAlign(TextAlign.Center) .backgroundColor(iconBg) .borderRadius(9) Text(label) .fontSize(16) .fontColor(isDanger ? #FF3B30 : #222222) // 危险色判断 .layoutWeight(1) } .width(100%) .padding({ left: 18, right: 18, top: 16, bottom: 16 }) }3.2 循环构建Builder 中可以配合ForEach使用Builder menuGroupList(groups: MenuGroup[]) { ForEach(groups, (group: MenuGroup) { Column({ space: 0 }) { Text(group.title) .fontSize(12).fontColor(COLOR_TEXT_HINT) .padding({ left: PAGE_PADDING, bottom: 6 }) Column() { ForEach(group.items, (item: MenuItemData, idx: number) { this.menuItemRow(item, idx) }) } } }) }3.3 Builder 嵌套调用Builder 可以嵌套调用其他 BuilderBuilder menuItemRow(item: MenuItemData, idx: number) { Row({ space: 12 }) { SymbolGlyph(item.icon).fontSize(18).fontColor([COLOR_AMBER]) Text(item.label).fontSize(15).fontColor(COLOR_TEXT_MAIN).layoutWeight(1) SymbolGlyph($r(sys.symbol.chevron_right)).fontSize(14).fontColor([COLOR_TEXT_HINT]) } .width(100%).height(50) .padding({ left: PAGE_PADDING, right: PAGE_PADDING }) .onClick(() router.pushUrl({ url: item.url })) } Builder menuGroupList(groups: MenuGroup[]) { ForEach(groups, (group: MenuGroup) { // 嵌套调用 menuItemRow ForEach(group.items, (item: MenuItemData, idx: number) { this.menuItemRow(item, idx) }) }) }四、BuilderParam 组件插槽4.1 什么是 BuilderParamBuilderParam用于定义组件插槽允许父组件向子组件注入自定义的 UI 构建逻辑。这类似于 React 的children或 Vue 的slot。4.2 WaveHeaderBand 的插槽设计“海风日记“的WaveHeaderBand组件通过BuilderParam实现了内容插槽Component export struct WaveHeaderBand { /** 品牌标语传空字符串则不显示 */ tagline: string — 海风日记 · 让每一天随海风飘散 — /** 问候语带 emoji */ greeting: string 海天之间藏着你的每一天 /** 自定义内容区 Builder */ BuilderParam content: () void this.defaultContent // 默认值 Builder defaultContent() {} build() { Stack({ alignContent: Alignment.Bottom }) { // 渐变背景 Column() { if (this.tagline.length 0) { Text(this.tagline).fontSize(11).fontColor(COLOR_AMBER) } Text(this.greeting).fontSize(12).fontColor(COLOR_AMBER_DARK) // 自定义内容插槽 this.content() } // ... } } }4.3 BuilderParam 的语法BuilderParam的定义语法// 1. 定义 BuilderParam 属性 BuilderParam content: () void this.defaultContent // 2. 定义默认构建函数 Builder defaultContent() {} // 3. 在 build 中调用插槽 build() { Column() { this.content() // 调用插槽 } }4.4 在父组件中使用 BuilderParam父组件通过尾随闭包语法传递自定义构建逻辑// 在 HomeTab 中使用 WaveHeaderBand WaveHeaderBand({ tagline: — 海风日记 · 让每一天随海风飘散 —, greeting: 海天之间藏着你的每一天, content: () { this.headerExtra() } // 传入自定义内容 })content参数接收一个箭头函数该函数将在子组件的插槽位置执行。4.5 BuilderParam 的常见模式模式说明示例默认插槽提供一个默认构建函数可选覆盖WaveHeaderBand的content具名插槽多个 BuilderParam 用于不同位置头部插槽、底部插槽条件插槽根据条件决定是否显示插槽内容空字符串时隐藏 tagline五、Builder 在 DiaryDetailPage 中的实战5.1 Bottom Sheet 操作菜单在DiaryDetailPage中使用 Builder 构建了操作菜单的每个选项Builder actionItem(icon: string, iconBg: string, label: string, isDanger: boolean, onTap: () void) { Row({ space: 14 }) { Text(icon) .fontSize(18) .width(36).height(36) .textAlign(TextAlign.Center) .backgroundColor(iconBg) .borderRadius(9) Text(label) .fontSize(16) .fontColor(isDanger ? #FF3B30 : #222222) .layoutWeight(1) SymbolGlyph($r(sys.symbol.chevron_right)) .fontSize(14).fontColor([#CCCCCC]) } .width(100%) .padding({ left: 18, right: 18, top: 16, bottom: 16 }) .onClick(() onTap()) }5.2 调用操作菜单项// 编辑日记 this.actionItem(✏️, #FFF3E0, 编辑日记, false, () { this.showActionSheet false router.pushUrl({ url: pages/diary/EditDiaryPage }) }) // 删除日记危险项 this.actionItem(️, #FFF0F0, 删除日记, true, () { this.showActionSheet false router.back() })5.3 操作菜单的完整构建Builder actionSheetOverlay() { Stack() { // 遮罩层 Column() .width(100%).height(100%) .backgroundColor(rgba(0,0,0,0.45)) .onClick(() { this.showActionSheet false }) // Bottom Sheet Column({ space: 8 }) { Column() { // 复用 actionItem 构建器 this.actionItem(✏️, #FFF3E0, 编辑日记, false, ...) Divider().color(#F5F5F5).margin({ left: 54 }) this.actionItem(, #E8F4FF, 分享日记, false, ...) Divider().color(#F5F5F5).margin({ left: 54 }) this.actionItem(, #F0F0F5, 设为私密, false, ...) Divider().color(#F5F5F5).margin({ left: 54 }) this.actionItem(, #F5FFF0, 复制文本, false, ...) this.actionItem(️, #FFF0F0, 删除日记, true, ...) } .backgroundColor(#FFFFFF).borderRadius(14) // 取消按钮 Text(取消) .width(100%).fontSize(17).fontColor(COLOR_PRIMARY) .backgroundColor(#FFFFFF).borderRadius(14) .padding(16) .onClick(() { this.showActionSheet false }) } .width(100%).padding({ left: 12, right: 12, bottom: 34 }) } .width(100%).height(100%) }六、Builder 在 MineTab 中的实战6.1 菜单项构建器MineTab中的菜单列表通过 Builder 和 ForEach 配合实现// 定义菜单分组数据 interface MenuItemData { icon: Resource label: string url: string danger?: boolean } interface MenuGroup { title: string items: MenuItemData[] }6.2 菜单分组渲染ForEach(this.menuGroups, (group: MenuGroup) { Column({ space: 0 }) { // 分组标题 Text(group.title) .fontSize(12).fontColor(COLOR_TEXT_HINT) .padding({ left: PAGE_PADDING, bottom: 6 }) // 分组内菜单项 Column() { ForEach(group.items, (item: MenuItemData, idx: number) { Row({ space: 12 }) { SymbolGlyph(item.icon) .fontSize(18) .fontColor([item.danger ? #D06060 : COLOR_AMBER]) Text(item.label) .fontSize(15) .fontColor(item.danger ? #D06060 : COLOR_TEXT_MAIN) .layoutWeight(1) SymbolGlyph($r(sys.symbol.chevron_right)) .fontSize(14).fontColor([COLOR_TEXT_HINT]) } .width(100%).height(50) .padding({ left: PAGE_PADDING, right: PAGE_PADDING }) .onClick(() router.pushUrl({ url: item.url })) if (idx group.items.length - 1) { Divider().color(COLOR_DIVIDER).margin({ left: PAGE_PADDING 30 }) } }) } .backgroundColor(COLOR_CARD_BG).borderRadius(CARD_RADIUS) } .margin({ bottom: 16 }) })6.3 数据驱动 vs 构建器模式对比两种复用方式方式优点缺点适用场景数据驱动ForEach 数据灵活、易扩展复杂的 UI 逻辑难表达列表展示Builder 构建器封装 UI 逻辑调用简单重复代码固定样式的 UI 片段七、Builder 的 StatCard 模式7.1 统计卡片构建器ReviewTab中的统计卡片也使用了 Builder 模式Builder statCard(value: string, label: string, color: string) { Column({ space: 4 }) { Text(value) .fontSize(26).fontColor(color).fontWeight(FontWeight.Bold) Text(label) .fontSize(11).fontColor(COLOR_TEXT_SECONDARY) } .layoutWeight(1).alignItems(HorizontalAlign.Center) .padding({ top: 16, bottom: 16 }) .backgroundColor(COLOR_CARD_BG) .borderRadius(CARD_RADIUS) .shadow({ radius: 8, color: rgba(160,120,60,0.08), offsetY: 2 }) }7.2 调用统计卡片Row({ space: 10 }) { this.statCard(20, 日记篇数, COLOR_PRIMARY) this.statCard(7, 连续天数, COLOR_AMBER) this.statCard(1.2K, 总字数, #C8956A) }7.3 Builder 与 Component 的选择策略选择 Builder 当选择 Component 当只需要复用 UI 构建逻辑需要独立的生命周期不需要状态管理需要独立的 State调用方需要传递参数参数来自父组件单向流入构建逻辑简单构建逻辑复杂八、BuilderParam 的高级用法8.1 多个 BuilderParam 插槽一个组件可以定义多个 BuilderParam 插槽Component export struct CardLayout { BuilderParam header: () void this.defaultHeader BuilderParam body: () void this.defaultBody BuilderParam footer: () void this.defaultFooter Builder defaultHeader() {} Builder defaultBody() {} Builder defaultFooter() {} build() { Column() { this.header() // 头部插槽 Divider() this.body() // 主体插槽 Divider() this.footer() // 底部插槽 } } }8.2 带参数的 BuilderParamBuilderParam 本身不支持参数传递但可以通过闭包捕获实现// 父组件中通过闭包传递参数 CardLayout({ header: () { Text(自定义标题).fontSize(18) }, body: () { // 通过闭包访问父组件的状态 Text(this.description).fontSize(14) } })8.3 BuilderParam 的默认值为 BuilderParam 提供默认值确保即使父组件不传参也能正常渲染// 定义默认构建函数 Builder defaultContent() { Text(默认内容) } // 使用默认值 BuilderParam content: () void this.defaultContent提示始终为 BuilderParam 提供默认值避免父组件未传参时出现空白区域。九、实际开发中的最佳实践9.1 Builder 命名规范// 建议的命名规范 Builder tabBuilder(...) // Tab 构建器 Builder actionItem(...) // 操作项构建器 Builder statCard(...) // 统计卡片构建器 Builder menuItemRow(...) // 菜单行构建器 Builder headerExtra(...) // 头部额外内容构建器9.2 性能优化避免在 Builder 中创建大量对象每次调用都会创建新的对象合理拆分 Builder过大的 Builder 应拆分为多个小构建器使用 const 数据不变的数据提取为常量减少重复创建9.3 常见错误在 Builder 中修改 StateBuilder 不应包含状态修改逻辑Builder 中调用异步方法Builder 是同步的不支持异步操作Builder 中创建组件实例Builder 应只包含 UI 构建逻辑总结本文通过“海风日记“的源码详细讲解了 Builder 和 BuilderParam 的完整用法Builder 基础自定义构建函数用于 UI 逻辑复用Tab 导航栏构建器统一构建 4 个 Tab 的完整样式操作菜单构建器封装 Bottom Sheet 中的操作项统计卡片构建器统一数据统计卡片样式BuilderParam 插槽WaveHeaderBand 组件的自定义内容区多插槽模式组件多个插槽的设计与使用性能优化合理使用 Builder 避免性能问题下一篇文章将深入讲解SymbolGlyph 系统图标在鸿蒙 App 中的统一使用包括图标着色、资源引用和最佳实践敬请期待。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源Builder 装饰器文档BuilderParam 装饰器文档ArkUI 组件化开发指南海风日记项目源码[HarmonyOS 开发者官网](https://atomgit.com/openharmony/docs开源鸿蒙跨平台社区ArkUI 状态管理SymbolGlyph API 文档