HarmonyOS 应用开发《掌上英语》第4篇:组件化设计原则:从 8 个业务组件看模块复用

发布时间:2026/7/11 20:27:24
HarmonyOS 应用开发《掌上英语》第4篇:组件化设计原则:从 8 个业务组件看模块复用 组件化设计原则从 8 个业务组件看模块复用一、引言在 HarmonyOS 应用开发中组件化是应对业务复杂度增长的核心手段。当项目从单模块向多模块演进时如何设计高内聚、低耦合的业务组件如何管理组件的构建与发布成为架构师必须回答的问题。本文以一个包含 11 个模块的 HarmonyOS 英语学习 App 为案例深入分析其components/目录下 8 个独立业务组件的设计模式涵盖独立模块配置、接口导出、状态边界控制和 HAR 包构建等关键主题。这 8 个组件分别是组件名称功能定位依赖关系base_select基础选择器无依赖select_category分类选择无依赖answer_questions答题引擎无依赖login_info登录模块无依赖aggregated_payment聚合支付无依赖feedback意见反馈依赖 axios、mocksearch搜索 UI无依赖search_question题目搜索无依赖其中 7 个组件零外部依赖体现了纯粹的组件化设计思想。二、独立模块的 build-profile.json5 配置每个组件都是一个独立的 HARHarmony Archive模块拥有自己的build-profile.json5文件。以base_select为例{ apiType: stageMode, buildOption: {}, buildOptionSet: [ { name: release, arkOptions: { obfuscation: { ruleOptions: { enable: false, files: [./obfuscation-rules.txt] }, consumerFiles: [./consumer-rules.txt] } } } ], targets: [ { name: default }, { name: ohosTest } ] }组件模块的配置要点如下1. apiType 固定为 stageModeHarmonyOS 从 API 9 开始全面推行 Stage 模型所有组件模块必须统一使用stageMode确保生命周期管理和上下文传递的一致性。2. consumerFiles 混淆规则作为 HAR 包组件需要提供consumer-rules.txt将混淆规则传递给最终消费者。这确保了组件内部的代码经过混淆保护同时对外暴露的接口保持可用。3. 极简的 buildOption组件模块无需关心签名、权限等应用级配置因此buildOption通常为空。配置的简化也是组件化的优势之一——聚焦业务逻辑而非工程配置。4. 项目级模块注册在根目录build-profile.json5中所有组件模块需要注册到modules数组{ modules: [ { name: base_select, srcPath: ./components/base_select }, { name: select_category, srcPath: ./components/select_category }, { name: answer_questions, srcPath: ./components/answer_questions }, { name: login_info, srcPath: ./components/login_info }, { name: aggregated_payment, srcPath: ./components/aggregated_payment }, { name: feedback, srcPath: ./components/feedback }, { name: search, srcPath: ./components/search }, { name: search_question, srcPath: ./components/search_question } ] }三、Index.ets 导出组件能力的模式每个组件模块的Index.ets是模块对外的唯一接口门面Facade这是模块化设计的第一原则接口先行实现隐藏。3.1 简单导出直接暴露组件最简单的模式如search组件仅导出一个页面组件// components/search/Index.etsexport{SearchPage}from./src/main/ets/components/SearchPage;search_question同样遵循此模式// components/search_question/Index.etsexport{SearchQuestionPage}from./src/main/ets/components/SearchQuestionPage;3.2 复合导出组件 数据模型更常见的做法是同时导出组件和数据模型让消费者既能使用 UI 组件也能引用类型定义。以base_select为例// components/base_select/Index.etsexport{BaseSelect,ContentItemModel,SelectItemModel,ThirdItemModel,SecondItemModel,ItemDetail,SecondParam}from./src/main/ets/components/MainPage;这里导出了 1 个组件和 6 个数据模型类型。消费者可以这样使用import{BaseSelect,SelectItemModel}frombase_select;3.3 全量导出复杂业务模块对于像answer_questions这样功能丰富的组件其 Index.ets 采用了全量导出策略// components/answer_questions/Index.etsexport*from./src/main/ets/components/AnswerQuestionsPage;export*from./src/main/ets/components/AddNotePage;export*from./src/main/ets/components/AnswerSheetPage;export{TopicItemModel,AnswerItem,TopicItemModelPreference,AnswerItemPreference}from./src/main/ets/viewModel/TopicItemModel;export{TopicItemType,AnswerItemType,QUESTION_DATA,WORD_QUESTION_DATA,LISTENING_QUESTION_DATA,READING_QUESTION_DATA,GRAMMAR_QUESTION_DATA,QuestionsRouterModel}from./src/main/ets/viewModel/TopicPageModel;export{AnswerSheetDialog}from./src/main/ets/dialog/AnswerSheetDialog;export{AddNoteDialog}from./src/main/ets/dialog/AddNoteDialog;这里导出了 3 个页面组件、2 个对话框、4 个数据模型和 7 个常量/枚举。特别值得注意的是QuestionsRouterModel——这是一个导航路由模型表明该组件具备独立的路由能力可以在内部管理自己的页面栈。3.4 能力导出服务 组件aggregated_payment组件展示了服务能力的导出模式// components/aggregated_payment/Index.etsexport{PaymentCallback,PaymentType}from./src/main/ets/common/CustomTypes;export{WechatPaymentInfo}from./src/main/ets/models/WechatPaymentInfo;export{HuaweiPaymentInfo}from./src/main/ets/models/HuaweiPaymentInfo;export{AlipayPaymentInfo}from./src/main/ets/models/AlipayPaymentInfo;export{CashierPicker}from./src/main/ets/CashierPicker;export{aggregatedPaymentService}from./src/main/ets/AggregatedPaymentService;不仅导出了 UI 组件CashierPicker还导出了聚合支付服务aggregatedPaymentService和多种支付模型。这种模式下组件不仅是 UI 的复用更是业务能力的复用。四、组件内部状态与外部参数的边界良好的组件化设计需要清晰划分内部状态和外部参数的边界。本项目使用 ArkTS V2 装饰器体系来实现这一划分。4.1 Param外部传入的参数以login_info组件的QuickLogin为例ComponentV2exportstruct QuickLogin{// 外部参数由父组件传入ParamisBindContentCover:booleanfalse;ParamRequireicon:ResourceStr;ParamRequireloginBtnBgColor:string|ResourceStr;ParamRequireappName:string;// 事件回调通知父组件EventonLoginWithHuaweiID:(flag:boolean,phone?:string)void(){};EventonPrivacyPolicy:()void(){};EventonServicePolicy:()void(){};EventshouldDismiss:()void(){};// 内部状态组件自己管理LocalisSelected:booleanfalse;LocalquickLoginAnonymousPhone:string;}设计原则清晰Param标注外部注入的参数使用Require标记必填项Event标注事件回调组件通过回调通知父组件状态变化Local标注内部状态外部不可直接干涉4.2 父组件如何使用在LoginPage中组件的使用方式如下ComponentV2exportstruct LoginPage{isBindContentCover:booleanfalse;build(){Column(){QuickLogin({isBindContentCover:this.isBindContentCover,icon:$r(app.media.exam_icon_login),loginBtnBgColor:#4B5CC4,appName:备考,onLoginWithHuaweiID:(flag:boolean,phone?:string){// 处理登录成功逻辑letprop:UserInfoAppStorageV2.connect(UserInfo,()newUserInfo())!;prop.isLogintrue;// ...},shouldDismiss:(){RouterModule.pop();}});}}}父组件通过传参控制组件的行为通过回调监听组件的事件两者之间没有直接的状态共享实现了彻底的解耦。4.3 参数设计最佳实践必填参数用 Require编译期检查防止遗漏提供合理的默认值非必填参数给出默认值降低使用成本回调用 Event语义清晰区别于 Param内部状态用 Local外部不可修改保持封装性复杂参数使用接口类型提高可读性和类型安全五、HAR 包的构建与引用5.1 构建 HAR 包HARHarmony Archive是 HarmonyOS 的静态共享包格式类似于 Android 的 AAR。当使用hvigor构建时组件模块默认输出.har文件components/base_select/build/default/outputs/default/base_select.harHAR 包的特点包含编译后的字节码和资源文件不包含签名信息由最终应用签名支持混淆通过 consumer-rules.txt支持资源合并5.2 引用方式在消费者模块的oh-package.json5中通过文件路径引用// product/entry/oh-package.json5 { dependencies: { login_info: file:../../components/login_info, answer_questions: file:../../components/answer_questions, feedback: file:../../components/feedback } }而homePagefeature 模块则引用了更多组件// features/homePage/oh-package.json5 { dependencies: { commonlib: file:../../commons/commonLib, login_info: file:../../components/login_info, search: file:../../components/search, search_question: file:../../components/search_question, answer_questions: file:../../components/answer_questions, base_select: file:../../components/base_select, select_category: file:../../components/select_category } }5.3 独立组件的依赖策略值得关注的是8 个组件中仅有feedback组件具有外部依赖// components/feedback/oh-package.json5 { dependencies: { ohos/axios: 2.2.6, hw-agconnect/axios-mock-adapter: 1.0.0, hw-agconnect/util-log: 1.0.0, hw-agconnect/ui-toast: 1.0.0 } }而base_select、answer_questions、login_info等 7 个组件的依赖为空// components/base_select/oh-package.json5 { dependencies: {} }这种零依赖设计是组件复用的最高境界——组件不依赖任何外部库或框架可以嫁接到任何项目中。实际开发中应尽可能保持组件的纯净性将外部依赖降到最低。六、最佳实践总结1. 独立配置独立构建每个组件拥有自己的build-profile.json5、oh-package.json5、hvigorfile.ts实现构建层面的完全独立。2. Index.ets 作为唯一出口所有对外暴露的能力通过 Index.ets 导出消费者禁止引用模块内部路径。3. 清晰的装饰器语义Param外部传入参数Event事件回调Local内部状态Require必填标记4. 最小依赖原则组件应尽可能零外部依赖必须的依赖通过oh-package.json5声明并由消费者传递。5. HAR 包的版本管理每个组件维护独立的CHANGELOG.md和LICENSE支持独立的版本迭代和发布。通过以上设计本项目实现了组件的最大化复用——同一个组件可以服务于多个 feature 模块甚至可以跨项目复用。当新业务需要类似功能时只需要引入对应的 HAR 包并传入参数即可无需重复开发。七、参考源码路径文件路径根模块配置d:\HarmonyOS\WorkSpace\Exam1.0.4\build-profile.json5base_select 模块d:\HarmonyOS\WorkSpace\Exam1.0.4\components\base_select\answer_questions 模块d:\HarmonyOS\WorkSpace\Exam1.0.4\components\answer_questions\login_info 模块d:\HarmonyOS\WorkSpace\Exam1.0.4\components\login_info\feedback 模块d:\HarmonyOS\WorkSpace\Exam1.0.4\components\feedback\aggregated_payment 模块d:\HarmonyOS\WorkSpace\Exam1.0.4\components\aggregated_payment\