HarmonyOS 应用开发《掌上英语》第17篇:声明式 UI 构建范式——ArkTS 的 @Builder 装饰器深入
2026/7/17 0:34:36 网站建设 项目流程

声明式 UI 构建范式——ArkTS 的 @Builder 装饰器深入

引言

在 HarmonyOS 的 ArkTS 声明式 UI 框架中,@Builder装饰器是一个极其重要的构建工具。它允许开发者将 UI 片段封装为可复用的构建函数,从而避免在build()方法中编写臃肿的嵌套代码。本文将通过我们英语学习 App 的实际源码,深入剖析@Builder的四种使用场景:组件内 Builder、全局 Builder、参数传递、以及导出复用。


一、@Builder 的基本概念

@Builder是 ArkTS 提供的 UI 构建函数装饰器,它的核心特性有:

  • 封装 UI 片段:将一个独立的 UI 区块封装成一个函数,在build()中直接调用
  • 支持两种定义位置:组件内部定义为成员方法,或组件外部定义为全局函数
  • 支持参数传递:可以通过参数向 Builder 传入数据
  • 具备响应式能力:Builder 内部引用组件的@Local/@Param变量时,变化会自动触发重绘

在我们的MainPage.ets中,总共使用了 6 个@Builder来组织首页的各个 UI 区块,形成了一套清晰的 UI 分层结构:

HomePage (主组件) ├── topBarBuilder() — 顶部导航栏 ├── todayStudyBuilder() — 今日学习卡片 ├── swiperBuilder() — 轮播 Banner ├── functionBarBuilder() — 5 宫格功能栏 ├── practiceModeBuilder() — 2×2 练习模式 └── subjectPracticeBuilder() — 学习进度区

二、组件内 @Builder

2.1 基础用法

组件内的@Builder方法可以直接访问当前组件的所有属性和状态变量。以下是我们HomePage中的topBarBuilder()

@ComponentV2exportstruct HomePage{// ...build(){Column(){// 直接调用 Builderthis.topBarBuilder();Scroll(){Column({space:10}){this.todayStudyBuilder();this.swiperBuilder();this.functionBarBuilder();this.practiceModeBuilder();this.subjectPracticeBuilder();}}// ...}}@BuildertopBarBuilder(){Row(){Text('英语学习').fontSize(20).fontWeight(FontWeight.Bold).layoutWeight(1);Image($r('app.media.ic_practice_camera')).width(28).height(28).onClick(()=>{RouterModule.push({url:RouterMap.SEARCH_INPUT_PAGE});});}.width('100%').height(56).padding({left:4,right:4});}// ...}

关键点解析:

  • Builder 内部直接使用this.functionsthis.dataSource等组件状态变量
  • 调用时只需写this.topBarBuilder(),无需传递任何参数
  • Builder 内引用的状态变量变化时,Builder 会自动触发重新渲染

2.2 访问组件状态的典型示例

todayStudyBuilder()展示了如何通过 Builder 读取组件数据并渲染 UI:

@BuildertodayStudyBuilder(){Column(){Row(){Text('今日学习').fontSize(16).fontWeight(FontWeight.Medium);Text('坚持 7 天').fontSize(12).fontColor($r('sys.color.font_secondary')).layoutWeight(1).textAlign(TextAlign.End);}.width('100%').margin({bottom:8});Row(){Text('已学 15 词 / 目标 30 词').fontSize(13).fontColor($r('sys.color.font_secondary')).layoutWeight(1);Text('50%').fontSize(20).fontWeight(FontWeight.Bold).fontColor($r('sys.color.comp_background_emphasize'));}.width('100%').margin({bottom:8});Progress({value:15,total:30,type:ProgressType.Linear}).color($r('sys.color.comp_background_emphasize')).height(8).width('100%');}.padding(16).borderRadius(16).backgroundColor($r('sys.color.background_primary'));}

这个 Builder 封装了一个完整的学习进度卡片,包含标题行、数据行和进度条,形成了一个独立的视觉模块。


三、全局 @Builder

当一个 Builder 需要被多个组件复用时,可以将其定义在组件外部,成为全局 Builder。

3.1 全局 Builder 的定义

MainPage.ets的末尾,我们定义了多个全局 Builder:

// 全局 Builder:直接返回一个组件实例@BuilderexportfunctionHomePageBuilder(){HomePage();}// 全局 Builder:封装核心功能图标@BuilderexportfunctionCourseCoreBuilder(mainFunctionItem:CourseCoreBar){Column(){Image(mainFunctionItem.showIcon).height($r('app.float.vp_24')).width($r('app.float.vp_24')).margin({bottom:$r('app.float.vp_2')});Text(mainFunctionItem.description).fontSize($r('sys.float.Caption_M'));}.width('100%').height(72).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).stateStyles({pressed:{.scale({x:0.97,y:0.97})}}).onClick(()=>{// 路由跳转逻辑// ...});}// 全局 Builder:封装课程练习卡片@BuilderexportfunctionCoursePracticeBuilder(questions:CourseQuestions){Column(){Row(){Text(questions.subjectName).fontSize($r('sys.float.Subtitle_S')).fontWeight(FontWeight.Medium);// ...}// ...}}

3.2 全局 Builder 的导出与导入

使用export关键字可以将 Builder 导出,供其他模块使用:

// 在 RouterTable 注册页面@BuilderexportfunctionHomePageBuilder(){HomePage();}

在路由表中,可以这样注册:

RouterTable.register('HomePage',wrapBuilder(HomePageBuilder));

同样在CourseHomePage.ets中也有类似用法:

@BuilderexportfunctionCourseHomePageBuilder(){CourseHomePage();}

四、@Builder 的参数传递

4.1 传递数据对象

全局 BuilderCourseCoreBuilder展示如何传递一个自定义对象:

@BuilderexportfunctionCourseCoreBuilder(mainFunctionItem:CourseCoreBar){Column(){Image(mainFunctionItem.showIcon).height($r('app.float.vp_24')).width($r('app.float.vp_24'));Text(mainFunctionItem.description).fontSize($r('sys.float.Caption_M'));}// ...}

functionBarBuilder中调用:

@BuilderfunctionBarBuilder(){Grid(){ForEach(this.functions,(item:CourseCoreBar)=>{GridItem(){// 传递 item 参数给全局 BuilderCourseCoreBuilder(item);};},(item:CourseCoreBar,index:number)=>item.description+index);}.columnsTemplate('1fr 1fr 1fr 1fr 1fr')// ...}

4.2 传递基础类型

全局 BuilderCoursePracticeBuilder接收CourseQuestions对象,展示了更复杂的数据传递:

@BuilderexportfunctionCoursePracticeBuilder(questions:CourseQuestions){Column(){Row(){Text(questions.subjectName).fontSize($r('sys.float.Subtitle_S')).fontWeight(FontWeight.Medium);};Row(){Text('开始学习').fontSize($r('sys.float.Caption_L')).fontWeight(FontWeight.Regular).fontColor($r('sys.color.font_secondary'));Image($r('app.media.icon_right')).width($r('app.float.vp_16')).height($r('app.float.vp_16'));}.layoutWeight(1).justifyContent(FlexAlign.End);Row(){Text('已掌握').margin({right:8}).fontSize($r('sys.float.Body_S'));Text(){Span(questions.masteredWords+'').fontColor($r('sys.color.comp_background_emphasize'));Span(' / '+questions.totalWords+'');}.fontSize($r('sys.float.Body_S'));}// ...}}

4.3 参数传递规范

参数类型示例说明
自定义对象CourseCoreBuilder(item)推荐,结构清晰
基础类型Text('hello')适用于简单场景
Resource 类型$r('app.media.ic_home')用于资源引用

五、复用粒度控制

5.1 何时使用 Builder vs 子组件

场景推荐方案原因
UI 片段 < 30 行@Builder轻量、无额外上下文开销
UI 片段 30-50 行@Builder或子组件视复用范围而定
UI 片段 > 50 行独立@Component便于维护和测试
需要在多页面复用全局@Builder+export模块化共享
需要独立生命周期独立@ComponentBuilder 不拥有生命周期

5.2 实际项目中的粒度选择

在我们的项目中,Banner 组件因为需要独立的响应式逻辑和生命周期,所以抽成了独立组件:

@BuilderswiperBuilder(){Banner();// 直接复用 Banner 子组件}

topBarBuildertodayStudyBuilder等 UI 片段因为相对简单(20-40 行),且仅在HomePage中使用,所以保留为组件内 Builder,保持了良好的封装性。


六、最佳实践

6.1 Builder 命名规范

  • 组件内 Builder 使用camelCase命名,以Builder结尾:topBarBuildertodayStudyBuilder
  • 全局 Builder 使用PascalCase命名,以Builder结尾:HomePageBuilderCourseCoreBuilder

6.2 避免 Builder 嵌套过深

如果 Builder 内部逻辑变得复杂,应将其拆解为多个小型 Builder:

// ❌ 不推荐:一个 Builder 包含太多逻辑 @Builder complexBuilder() { /* 100 行代码 */ } // ✅ 推荐:拆分为多个小型 Builder @Builder partA() { /* 30 行 */ } @Builder partB() { /* 30 行 */ }

6.3 导出 Builder 的最佳实践

在路由注册场景中,习惯使用全局 Builder 包装页面组件:

// HomePage.ets 末尾@BuilderexportfunctionHomePageBuilder(){HomePage();}// CourseHomePage.ets 末尾@BuilderexportfunctionCourseHomePageBuilder(){CourseHomePage();}

这种方式比直接导出组件更灵活,允许在实际调用时注入参数或包装逻辑。


总结

@Builder装饰器是 ArkTS 声明式 UI 开发的核心工具之一。通过本文的源码分析,我们可以看到:

  1. 组件内 Builder适合封装单页面内的 UI 片段,直接访问组件状态
  2. 全局 Builder支持跨页面复用,通过export导出
  3. 参数传递让 Builder 具备数据驱动能力,支持自定义对象和基础类型
  4. 粒度控制根据复杂度和复用范围,在 Builder 和子组件之间做合理选择

用好@Builder,能让你的 ArkTS 代码保持清爽、模块化和可维护。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询