
第6.1篇多模块架构设计——模块拆分与依赖管理难度⭐⭐⭐ 高级前置知识第 1.1 篇 ArkTS 入门涉及源文件build-profile.json5根、oh-package.json5根、products/default/oh-package.json5、products/default/module.json5、common/oh-package.json5、features/adaptiveLayout/oh-package.json5、features/responsiveLayout/oh-package.json5、common/Index.ets一、概述在画伴梦工厂项目中随着功能不断扩展——自适应布局、响应式布局、语音识别、3D 渲染、服务卡片等——单模块架构逐渐暴露出以下问题痛点表现编译速度慢任何微小改动都需要重新编译整个工程耦合严重工具函数、常量、类型定义散落在各个页面目录下难以复用职责边界模糊布局逻辑与业务逻辑混在一起修改一个功能可能影响其他功能不利于团队协作多人同时修改同一模块极易产生代码冲突为了解决这些问题项目采用了多模块Multi-Module架构。多模块架构是鸿蒙工程中一种成熟的代码组织方式它将代码按职责拆分为多个独立的模块每个模块拥有自己的oh-package.json5依赖声明和build-profile.json5注册项模块之间通过file:协议建立显式的依赖关系。本文将深入分析画伴梦工厂的多模块工程结构从build-profile.json5的模块注册、oh-package.json5的跨模块依赖声明、module.json5的模块配置等维度完整阐述鸿蒙多模块架构的设计思路与最佳实践。二、四模块架构总览项目共拆分为4 个模块职责划分如下drawing-main/ ├── common/ # 共享基础库Shared Library │ └── oh-package.json5 # name: ohos/common ├── features/ │ ├── adaptiveLayout/ # 自适应布局特性Feature Library │ │ └── oh-package.json5 # name: ohos/adaptivelayout │ └── responsiveLayout/ # 响应式布局特性Feature Library │ └── oh-package.json5 # name: ohos/responsivelayout └── products/ └── default/ # 主应用入口模块Entry Module ├── oh-package.json5 # 依赖所有其他模块 ├── module.json5 # type: entry └── src/main/ets/ # 应用代码2.1 common —— 共享基础库common模块是所有其他模块的公共依赖。它不包含任何业务逻辑只提供项目范围内的共享基础能力能力文件说明Loggercommon/src/main/ets/utils/Logger.ets基于kit.PerformanceAnalysisKit的日志封装提供 debug/info/warn/error 分级输出BreakpointSystemcommon/src/main/ets/utils/BreakpointSystem.ets基于mediaquery的断点系统监测屏幕尺寸变化并同步到AppStorageBreakPointTypeT同上文件的导出类泛型工具根据当前断点名称返回对应的配置值common模块通过Index.ets作为统一出口导出所有公共 API// common/Index.etsexport{Logger}from./src/main/ets/utils/Logger;export{BreakpointSystem,BreakPointType}from./src/main/ets/utils/BreakpointSystem;2.2 adaptiveLayout —— 自适应布局特性adaptiveLayout模块封装了基于BreakpointSystem的自适应布局组件和工具函数。它依赖common模块获取断点状态并对外暴露adaptiveLayout组件// features/adaptiveLayout/Index.etsexport{adaptiveLayout}from./src/main/ets/pages/AdaptiveLayout;2.3 responsiveLayout —— 响应式布局特性responsiveLayout模块封装了基于GridRow/GridCol的响应式网格布局组件。同样依赖common模块对外暴露responsiveLayout组件// features/responsiveLayout/Index.etsexport{responsiveLayout}from./src/main/ets/pages/ResponsiveLayout;2.4 default —— 主应用入口default模块是type: entry的主应用模块包含所有页面、Ability、服务卡片、权限声明等。它是唯一可以打包运行的模块聚合了所有特性模块的能力。三、模块依赖关系图四个模块之间的依赖关系形成一个清晰的有向无环图DAG┌──────────────────────────────────────┐ │ default (entry) │ │ products/default/oh-package.json5 │ └──────┬────────────────────┬───────────┘ │ │ depends on depends on │ │ ▼ ▼ ┌────────────────────┐ ┌────────────────────────┐ │ adaptiveLayout │ │ responsiveLayout │ │ (feature library) │ │ (feature library) │ └────────┬───────────┘ └───────────┬────────────┘ │ │ depends on depends on │ │ ▼ ▼ ┌──────────────────────────────────────────┐ │ common (shared) │ │ ohos/common : 1.0.0 │ └──────────────────────────────────────────┘这个依赖图遵循了单向依赖原则common不依赖任何模块adaptiveLayout和responsiveLayout只依赖commondefault依赖所有其他模块任何两个模块之间都不存在循环依赖这是多模块架构能够正常编译的基本前提。四、根 build-profile.json5 —— 模块注册中心build-profile.json5是鸿蒙工程的模块注册配置文件它声明了项目包含的所有模块及其路径。在画伴梦工厂中根build-profile.json5的核心结构如下{ app: { products: [{ name: default, targetSdkVersion: 6.0.0(20), compatibleSdkVersion: 6.0.0(20), runtimeOS: HarmonyOS }], buildModeSet: [{ name: debug }, { name: release }] }, modules: [ { name: default, srcPath: ./products/default }, { name: common, srcPath: ./common }, { name: adaptiveLayout, srcPath: ./features/adaptiveLayout }, { name: responsiveLayout, srcPath: ./features/responsiveLayout } ] }4.1 modules 数组每一个modules条目定义了一个模块字段说明name模块名称在项目中必须唯一用于在构建系统中标识模块srcPath模块源码根目录的相对路径相对于项目根目录注意模块的name与oh-package.json5中的name字段可以不同。build-profile.json5中的name是构建系统使用的标识符而oh-package.json5中的name是跨模块依赖时引用的包名。4.2 app.products 与 app.buildModeSetproducts定义了产品的构建产物配置。targetSdkVersion和compatibleSdkVersion分别指定目标 SDK 版本和兼容 SDK 版本。runtimeOS指定运行的操作系统。buildModeSet定义可用的构建模式包括debug调试模式包含调试符号和日志和release发布模式混淆和优化后发布。五、oh-package.json5 跨模块依赖声明5.1 根 oh-package.json5根目录的oh-package.json5声明了项目全局的依赖{ modelVersion: 6.0.0, description: Please describe the basic information., dependencies: {}, devDependencies: { ohos/hypium: 1.0.24, ohos/hamock: 1.0.0 } }devDependencies中的ohos/hypium和ohos/hamock是测试框架依赖仅在开发环境使用不会打包到最终产物中。5.2 entry 模块的依赖声明products/default/oh-package.json5声明了主模块对三个子模块的依赖{ name: default, version: 1.0.0, main: , dependencies: { ohos/adaptivelayout: file:../../features/adaptiveLayout, ohos/responsivelayout: file:../../features/responsiveLayout, ohos/common: file:../../common } }这里的file:协议是鸿蒙工程中跨模块依赖的关键语法。file:../../features/adaptiveLayout表示依赖指向features/adaptiveLayout目录下的模块构建系统会根据该目录下的oh-package.json5中的name字段解析包名。5.3 common 模块的依赖声明{ name: ohos/common, version: 1.0.0, main: Index.ets, dependencies: {} }关键点name为ohos/common这是其他模块依赖它时使用的包名main指定为Index.ets这是模块的入口文件其他模块通过import { ... } from ohos/common导入时实际上导入的是Index.ets中导出的内容dependencies为空common是依赖链的最底层5.4 特性模块的依赖声明以adaptiveLayout为例{ name: ohos/adaptivelayout, version: 1.0.0, main: Index.ets, dependencies: { ohos/common: file:../../common } }responsiveLayout的配置与之类似同样依赖ohos/common。5.5 file: 协议详解file:协议是 HarmonyOS 提供的本地模块引用机制语法为file:相对路径。其核心特性包括特性说明路径解析相对路径从当前oh-package.json5所在目录开始计算自动依赖传递依赖 A 的模块自动获得 A 所依赖的 B 的能力无需额外声明编译时链接构建系统在编译阶段将本地模块的代码链接到最终产物中版本无关本地模块的version字段不影响依赖解析始终使用本地源码六、module.json5 模块配置详解products/default/src/main/module.json5是entry 模块的核心配置文件它定义了模块的类型、支持的设备、页面路由、Ability 组件以及权限声明{ module: { name: default, type: entry, description: $string:module_desc, mainElement: DefaultAbility, deviceTypes: [phone, tablet, 2in1], deliveryWithInstall: true, installationFree: false, pages: $profile:main_pages, requestPermissions: [ { name: ohos.permission.INTERNET }, { name: ohos.permission.CAMERA, reason: $string:camera_reason, usedScene: { abilities: [DefaultAbility], when: inuse } }, { name: ohos.permission.MICROPHONE, reason: $string:microphone_reason, usedScene: { abilities: [DefaultAbility], when: inuse } }, { name: ohos.permission.DISTRIBUTED_DATASYNC, reason: $string:distributed_data_reason, usedScene: { abilities: [DefaultAbility], when: inuse } } ], abilities: [ { name: DefaultAbility, srcEntry: ./ets/defaultability/DefaultAbility.ets, description: $string:ability_desc, icon: $media:layered_image, label: $string:ability_label, startWindowIcon: $media:startIcon, startWindowBackground: $color:start_window_background, exported: true, skills: [{ entities: [entity.system.home], actions: [ohos.want.action.home] }] } ], extensionAbilities: [ { name: CreationFormAbility, srcEntry: ./ets/creationformability/CreationFormAbility.ets, type: form, metadata: [{ name: ohos.extension.form, resource: $profile:form_config }] }, { name: DefaultBackupAbility, srcEntry: ./ets/defaultbackupability/DefaultBackupAbility.ets, type: backup, exported: false, metadata: [{ name: ohos.extension.backup, resource: $profile:backup_config }] } ] } }6.1 关键字段解读字段值含义namedefault模块名称与build-profile.json5中的name对应typeentry模块类型entry 表示可独立运行的应用入口模块deviceTypes[phone, tablet, 2in1]目标设备类型支持手机、平板和二合一设备pages$profile:main_pages页面路由配置指向 resources/base/profile/main_pages.jsonmainElementDefaultAbility应用入口 Ability6.2 requestPermissions —— 安全权限声明项目声明了四种权限INTERNET网络访问权限用于图片上传和 API 调用CAMERA相机权限用于拍照采集画作仅在inuse前台使用时申请MICROPHONE麦克风权限用于语音识别功能DISTRIBUTED_DATASYNC分布式数据同步权限用于跨设备数据共享每个危险权限都附带了reason字符串资源在权限申请弹窗中向用户说明使用目的这符合鸿蒙的安全最佳实践。6.3 extensionAbilities —— 扩展能力CreationFormAbilitytype:form服务卡片 Ability提供桌面小组件功能DefaultBackupAbilitytype:backup数据备份 Ability支持应用数据的云备份与恢复七、模块类型详解entry / feature / shared在鸿蒙多模块架构中模块按职责分为三种类型7.1 entry 模块{ type: entry }用途应用的主入口模块包含 Ability、页面路由、权限声明等应用级配置特性只能有一个 entry 模块在build-profile.json5的products中定义必须包含module.json5且type为entry可以引用所有 feature 和 shared 模块最终生成可安装的 HAP 包在项目中products/default就是 entry 模块它负责聚合所有特性模块并提供最终的 UI 页面和应用生命周期管理。7.2 feature 模块feature 模块没有独立的module.json5而是通过oh-package.json5声明自身并提供导出。用途封装独立的业务特性或功能如自适应布局、响应式布局特性可以有多个 feature 模块可以依赖 shared 模块和其他 feature 模块不包含 Ability 或页面路由配置通过Index.ets对外暴露 API项目中features/adaptiveLayout和features/responsiveLayout都是 feature 模块两者职责清晰、互不依赖。7.3 shared 模块用途提供跨模块共享的基础能力如工具类、常量、日志等特性可以有多个 shared 模块不应依赖任何 feature 模块保持最底层位置通过Index.ets作为模块入口统一导出项目中的common模块是典型的 shared 模块它不依赖任何其他业务模块其他所有模块都可以引用它。八、Index.ets —— 模块入口约定在鸿蒙多模块架构中每个通过oh-package.json5导出的模块都需要指定一个入口文件main字段。common模块的入口文件如下// common/Index.etsexport{Logger}from./src/main/ets/utils/Logger;export{BreakpointSystem,BreakPointType}from./src/main/ets/utils/BreakpointSystem;这种做法形成了清晰的门面模式Facade Pattern对外隐藏内部实现细节其他模块只需关心Index.ets中导出了什么无需了解utils/目录的组织结构统一引用入口import { Logger } from ohos/common即可获取日志能力控制可访问性Index.ets中只导出需要暴露的 API内部类和常量不对外可见九、common 模块核心能力剖析9.1 Logger —— 统一日志服务import{hilog}fromkit.PerformanceAnalysisKit;exportclassLogger{privatereadonlyprefix:stringtestTag;privatereadonlydomain:number0xFF00;privatereadonlyformat:string%{public}s, %{public}s;publicdebug(...args:string[]):void{hilog.debug(this.domain,this.prefix,this.format,args);}publicinfo(...args:string[]):void{hilog.info(this.domain,this.prefix,this.format,args);}publicwarn(...args:string[]):void{hilog.warn(this.domain,this.prefix,this.format,args);}publicerror(...args:string[]):void{hilog.error(this.domain,this.prefix,this.format,args);}}Logger封装了鸿蒙的hilog日志接口提供了debug、info、warn、error四个等级的输出方法。所有模块统一使用这个 Logger避免了日志输出格式不一致的问题。9.2 BreakpointSystem —— 媒体查询断点系统BreakpointSystem是自适应布局的核心基础设施。它通过mediaquery.MediaQueryListener监听屏幕宽度变化并在断点切换时更新AppStorage中的全局状态exportclassBreakpointSystem{privatereadonlybreakpoints:Breakpoint[][{name:sm,size:320},{name:md,size:600},{name:lg,size:840},{name:xl,size:1080}];publicregister(uiContext:UIContext):void{// 为每个断点范围注册媒体查询监听器this.breakpoints.forEach((breakpoint,index){letcondition;if(indexthis.breakpoints.length-1){condition(${breakpoint.size}vpwidth);}else{condition(${breakpoint.size}vpwidth${this.breakpoints[index1].size}vp);}// ... 注册监听});}}common模块还将BreakPointTypeT泛型工具类一并导出使得特性模块可以根据当前断点名称获取对应的配置值如间距、字号等实现真正的响应式布局。9.3 为什么不把这些放在 entry 模块将 Logger 和 BreakpointSystem 放在common模块而非default模块是基于以下架构考量避免循环依赖如果 Logger 放在default模块中那么adaptiveLayout要使用 Logger 就必须依赖default而default又依赖adaptiveLayout形成循环依赖违反 DAG 原则。模块复用性common模块可以独立复用。如果将来项目需要增加一个wearable产品线只需在build-profile.json5中注册新的 entry 模块并复用已有的common和features/*模块。编译效率common模块的内容不常变更编译一次后可以被缓存后续重新编译其他模块时无需重新编译common显著提升增量编译速度。十、架构决策原理与最佳实践10.1 为什么选择多模块架构单模块架构不采用 多模块架构采用 ┌─────────────────────┐ ┌─────────────────────┐ │ 所有代码混在一起 │ │ clear: entry │ │ 大到难以维护 │ │ │ │ │ 编译需要 5 分钟 │ │ ├─ feature: 布局 │ │ 改一行全局重编 │ │ ├─ feature: 语音 │ │ 新人上手困难 │ │ └─ shared: common │ └─────────────────────┘ │ 编译只需 1-2 分钟 │ │ 模块职责清晰 │ │ 可独立开发和测试 │ └─────────────────────┘10.2 模块拆分原则在实际项目中建议遵循以下原则进行模块拆分原则说明项目中的体现单一职责每个模块只做一件事common只做共享基础库adaptiveLayout只做自适应布局无环依赖依赖图必须为 DAGcommon ← adaptiveLayout ← default接口隔离通过Index.ets控制导出范围只导出需要公开的 API稳定方向依赖更稳定的模块被更低层依赖common最稳定位于底层共同复用一起变化的类放在同一模块布局相关组件集中在各自 feature 模块10.3 常见的架构陷阱陷阱一过度拆分模块的数量不是越多越好。通常 3~8 个模块对于中型项目是比较合理的区间。如果模块粒度过细每个工具类一个模块反而会增加依赖管理的复杂度。陷阱二循环依赖鸿蒙构建系统不支持循环依赖。如果 A 依赖 B、B 依赖 C、C 又依赖 A编译会直接报错。解决方法是将循环双方的共同依赖抽离到一个新的 shared 模块中。陷阱三跨模块直接引用内部文件// ❌ 错误做法——跨模块引用内部路径import{Logger}from../../common/src/main/ets/utils/Logger;// ✅ 正确做法——通过模块名引用import{Logger}fromohos/common;直接引用内部路径会绕过模块的Index.ets入口导致模块的封装性被破坏。如果以后重构内部文件结构所有直接引用的地方都需要修改。10.4 构建性能优化多模块架构带来的一个核心收益是增量编译。在鸿蒙 DevEco Studio 中当修改features/adaptiveLayout的代码时构建系统检测到adaptiveLayout的源文件发生变更仅重新编译adaptiveLayout模块及其直接依赖者如defaultcommon和responsiveLayout模块无需重新编译对于大型项目这种增量编译可以将构建时间从数分钟降低到数十秒。总结本文通过画伴梦工厂的实际项目代码完整分析了鸿蒙多模块架构的设计与实现知识点对应文件/配置核心要点模块注册build-profile.json5modules数组注册所有模块products定义构建产物依赖声明oh-package.json5file:协议声明本地模块依赖name/main定义模块入口模块配置module.json5type: entry定义入口模块deviceTypes指定支持设备模块类型三者搭配使用entry主应用、feature特性库、shared共享库模块入口Index.ets门面模式统一导出隐藏内部实现共享能力common模块提供 Logger、BreakpointSystem 等跨模块基础设施依赖图整体结构DAG 单向依赖无循环引用最佳实践架构原则单一职责、接口隔离、稳定方向依赖多模块架构是鸿蒙工程从能跑就行迈向可维护、可扩展的关键一步。它为项目的持续迭代奠定了坚实的工程基础也是团队协作开发的必备架构模式。下一篇第 6.2 篇将探讨鸿蒙工程的构建配置进阶——多产物构建、签名管理与 CI/CD 流水线集成敬请期待。