HarmonyOS 多模块工程实践:从零搭建多模块应用

在这里插入图片描述

一、引言

在大型 HarmonyOS 应用开发中,合理的工程结构是保证代码可维护性、团队协作效率和编译性能的关键。本文以一款英语学习 App 为例,深入剖析如何从零搭建一个包含 11 个模块的分层架构项目。

该应用采用 entry → features → commons → components 四层架构,共管理 11 个独立模块。通过清晰的模块边界和依赖规则,实现了业务逻辑的高内聚低耦合。无论你是正在重构单体应用,还是从零开始新项目,本文的多模块工程实践都值得借鉴。

二、模块架构概览

2.1 四层架构设计

我们的模块采用分层设计,每一层有明确的职责和依赖方向:

product/entry          → 应用入口层(装配所有模块)
features/              → 业务特征层(homePage、minePage、topicPage)
commons/commonLib      → 通用能力层(工具库、模型、路由)
components/            → 业务组件层(8 个独立业务组件)

依赖规则:上层可以依赖下层,下层不可反向依赖上层。具体来说:

  • entry 依赖 featurescommonscomponents
  • features 依赖 commonscomponents
  • components 可依赖 commons
  • commons 不依赖任何业务模块

2.2 11 个模块清单

层级 模块名 路径 职责
entry entry ./product/entry 应用入口、Ability、首页 Tab 装配
features homePage ./features/homePage 首页相关页面
features minePage ./features/minePage 个人中心相关页面
features topicPage ./features/topicPage 专题学习相关页面
commons commonLib ./commons/commonLib 通用工具、模型、管理器
components base_select ./components/base_select 基础选择组件
components select_category ./components/select_category 分类选择组件
components answer_questions ./components/answer_questions 答题组件
components login_info ./components/login_info 登录信息组件
components aggregated_payment ./components/aggregated_payment 聚合支付组件
components feedback ./components/feedback 反馈组件
components search ./components/search 搜索组件
components search_question ./components/search_question 搜索题目组件

三、核心配置文件解析

3.1 build-profile.json5 — 模块声明与依赖

项目的灵魂文件 build-profile.json5 中声明了所有模块的路径映射:

{
  "modules": [
    { "name": "entry", "srcPath": "./product/entry" },
    { "name": "homePage", "srcPath": "./features/homePage" },
    { "name": "minePage", "srcPath": "./features/minePage" },
    { "name": "topicPage", "srcPath": "./features/topicPage" },
    { "name": "commonLib", "srcPath": "./commons/commonLib" },
    { "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" }
  ]
}

关键点

  • name 是模块的唯一标识,后续在 oh-package.json5 中依赖时需要引用此名称
  • srcPath 指向模块源码目录,entry 模块放在 ./product/ 下,与业务代码物理隔离
  • 只有 entry 模块配置了 targets,因为它需要生成最终的应用产物

3.2 oh-package.json5 — 模块级依赖管理

每个模块都有自己的 oh-package.json5,通过 file: 协议引用本地模块。

应用级 oh-package.json5(根目录):

{
  "dependencies": {
    "dayjs": "1.11.13",
    "@hw-agconnect/util-log": "1.0.0",
    "aggregated_payment": "file:./components/aggregated_payment"
  }
}

entry 模块 oh-package.json5(装配所有模块):

{
  "name": "entry",
  "dependencies": {
    "@hw-agconnect/util-log": "1.0.0",
    "homepage": "file:../../features/homePage",
    "topicpage": "file:../../features/topicPage",
    "minepage": "file:../../features/minePage",
    "commonlib": "file:../../commons/commonLib",
    "login_info": "file:../../components/login_info",
    "answer_questions": "file:../../components/answer_questions",
    "feedback": "file:../../components/feedback"
  }
}

commonLib 模块 oh-package.json5

{
  "name": "commonlib",
  "main": "Index.ets",
  "dependencies": {
    "answer_questions": "file:../../components/answer_questions"
  }
}

依赖关系图

entry
  ├── homePage (features)
  ├── topicPage (features)
  ├── minePage (features)
  ├── commonLib (commons)
  │   └── answer_questions (components)
  ├── login_info (components)
  ├── answer_questions (components)
  └── feedback (components)

四、模块职责详解

4.1 commonLib — 通用能力层

commonLib 是整个应用的"工具箱",它不依赖任何业务模块。主要提供以下能力:

  • 工具类:Logger(日志)、PreferenceUtil(持久化)、AudioPlayer(语音播放)、RouterModule(路由管理)
  • 数据模型:WordCard(单词卡片)、BreakpointModel(断点模型)、BaseViewModel(基类 ViewModel)
  • 管理器:NewWordManager(生词管理)、StatisticsManager(统计)、DashboardManager(仪表盘)
  • UI 组件:CommonHeader、TopBar、Banner

4.2 features 层 — 业务特征模块

三个 feature 模块分别对应底部 Tab 的三个页面:

  • homePage:首页,包含 FeaturedCourses(推荐课程)、MaterialDownload(资料下载)、ChapterPractice(章节练习)等
  • topicPage:专题学习页,包含 CourseHomePage(卡片翻转学习)、AnswerQuestionsPage(答题)、WordCardPage(单词卡片)、DailyChallengePage(每日挑战)等
  • minePage:个人中心,包含 MinePage(我的)、SetUpPage(设置)、PracticeRecords(练习记录)、DashboardPage(仪表盘)等

每个 feature 模块通过 Index.ets 导出 Builder 函数,供 entry 层装配使用。

4.3 components 层 — 业务组件

8 个业务组件模块是细粒度的功能单元,可被 features 和 entry 层复用:

  • answer_questions:答题引擎(选择题、判断题、填空题)
  • base_select:基础选择器(单选、多选)
  • select_category:分类选择器
  • login_info:登录信息管理
  • aggregated_payment:聚合支付(支付宝、微信等)
  • feedback:用户反馈
  • search:搜索功能
  • search_question:题目搜索

4.4 entry — 应用装配入口

entry 模块作为应用的"总装车间",负责:

  1. 初始化路由表:在 EntryAbility.onCreate() 中调用 RouterTable.routerInit() 注册所有页面
  2. 注册断点监听:在 Index.aboutToAppear() 中调用 registerBreakpointListener()
  3. 加载主页面:通过 Navigation + NavPathStack 实现声明式路由
// EntryAbility.ets
export default class EntryAbility extends UIAbility {
  onCreate(): void {
    RouterTable.routerInit(); // 初始化所有路由
  }
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err) => { ... });
  }
}
// Index.ets — 应用根页面
@Entry
@ComponentV2
struct Index {
  aboutToAppear() {
    GlobalContextUtils.setUIContext('entry', this.getUIContext());
    this.mainVM.registerBreakpointListener(this.ctx.windowStage.getMainWindowSync());
    // 首次启动 → 隐私协议页;否则 → 主页
    let isFirstLaunch = PreferenceUtil.getInstance().get(PreferConstant.FIRST_LAUNCH, true);
    if (isFirstLaunch) {
      RouterModule.replace({ url: RouterMap.PRIVACY_AGREEMENT_PAGE });
    } else {
      RouterModule.replace({ url: RouterMap.MAIN_PAGE });
    }
  }

  build() {
    Column() {
      Navigation(RouterModule.stack) {
      }
      .hideNavBar(true)
      .navDestination(this.pageMap)
      .width('100%').height('100%');
    }
  }
}

五、最佳实践

5.1 模块划分原则

  1. 按业务边界拆分:每个模块应该有一个清晰的业务边界。例如,支付相关的逻辑全部封装在 aggregated_payment 中,其他模块只需通过接口调用。

  2. 避免循环依赖commonLib 虽然依赖了 answer_questions,但这种依赖应有明确理由。一般情况下,commons 层应该保持纯洁,不依赖任何业务组件。

  3. 控制模块粒度:模块太多会增加构建和依赖管理的复杂度。8 个组件模块中,有些(如 searchsearch_question)可以考虑合并。

5.2 依赖管理建议

  • 使用 file: 协议引用本地模块,路径使用相对路径
  • 避免在多个模块中重复声明相同的三方依赖(如 dayjs),将其提升到根 oh-package.json5
  • entry 层应作为"胶水代码",只做装配不做业务

5.3 编译优化技巧

  • 修改模块间接口时,优先在 Index.ets 中做重导出,而不是直接修改内部文件
  • 合理配置 build-profile.json5 中的 buildOption,开启严格模式确保代码质量
"buildOption": {
  "strictMode": {
    "caseSensitiveCheck": true,
    "useNormalizedOHMUrl": true
  }
}

六、总结

本文详细介绍了 HarmonyOS 多模块工程的四层架构设计,从 build-profile.json5 的模块声明到 oh-package.json5 的依赖管理,再到每个模块的具体职责。通过这种分层解耦的架构设计,我们实现了:

  • 更好的团队协作:不同团队可以并行开发不同模块
  • 更高的代码复用率:commonLib 和 components 模块可被多个 feature 复用
  • 更快的构建速度:增量编译时只需重新编译变更的模块
  • 更清晰的架构边界:依赖规则强制执行,防止架构腐化

在下一篇《ArkTS 模块化开发:Index.ets 统一导出模式深度解析》中,我们将深入探讨模块接口设计的最佳实践。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐