1. 项目背景与核心价值
去年接手公司鸿蒙应用的重构任务时,我面对的是一个超过5万行代码的庞然大物。所有业务逻辑都堆砌在entry模块里,每次修改功能都像在雷区排爆。经过三个月的模块化改造,最终拆解出17个独立模块,编译速度提升40%,团队协作效率翻倍。这次实战让我深刻体会到:模块化不是架构师的纸上谈兵,而是每个鸿蒙开发者必备的生存技能。
鸿蒙的模块化设计比Android更彻底,从编译层面就支持HAP(Harmony Ability Package)的独立部署。但官方文档对实际工程落地的指导有限,特别是如何处理模块间通信、资源冲突这些"坑点"。本文将分享从单entry到多模块的完整改造过程,包含一个电商App的实战Demo(已开源),演示如何解决以下核心问题:
- 业务模块如何划分才合理?按功能还是按团队?
- 模块间跳转如何避免强耦合?
- 公共资源如何管理才能避免重复打包?
- 开发阶段如何实现模块独立调试?
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 模块化架构设计原则
2.1 模块类型划分标准
鸿蒙工程中通常包含四种模块类型:
bash复制entry # 主入口模块
feature-* # 业务功能模块(如feature-cart)
library-* # 公共库模块(如library-network)
common # 通用资源模块
我们采用的拆分原则是:
- 垂直分层:每个业务线拥有独立的feature模块(如支付、商品详情)
- 能力下沉:网络请求、图片加载等基础能力抽离到library
- 资源收敛:公共字符串、颜色值统一放在common模块
关键经验:先按团队职责划分模块边界,再考虑功能聚合。比如由A团队负责的"购物车"和"订单"应该拆分为两个模块,尽管它们业务关联紧密。
2.2 依赖关系管理
模块间依赖必须遵循单向数据流原则:
code复制entry → feature → library
↘ common ↗
通过gradle配置强制约束:
groovy复制// feature-cart/build.gradle
dependencies {
implementation project(':library-network')
compileOnly project(':common') // 避免资源重复打包
}
3. 关键实现技术点
3.1 模块间通信方案
方案对比表:
| 方式 | 适用场景 | 示例代码 | 优缺点 |
|---|---|---|---|
| 隐式Want | 跨应用跳转 | want.operation = "detail" |
灵活性高但性能差 |
| 公共事件 | 状态广播 | CommonEvent.publish("EVENT_LOGIN") |
适合一对多通知 |
| 接口暴露 | 模块能力复用 | 见下方示例 | 类型安全但需要显式注册 |
接口暴露最佳实践:
- 在library模块定义接口:
java复制// library-account/export/IAccountService.ets
export interface IAccountService {
getUserInfo(): Promise<User>;
}
- 在feature模块实现:
java复制// feature-account/impl/AccountServiceImpl.ets
export default class AccountService implements IAccountService {
// 实现细节...
}
- 通过DI容器注册(使用ohos.rpc实现):
typescript复制// 初始化时注册
AbilityContext.registerAbility('ACCOUNT_SERVICE', new AccountService())
// 调用时获取
const service = AbilityContext.getAbility('ACCOUNT_SERVICE') as IAccountService
3.2 资源冲突解决方案
当多个模块包含同名资源时,编译会报错"Resource conflict"。我们采用的解决方案:
- 前缀命名法:
xml复制<!-- 在feature-cart/resources/base/element/strings.json -->
{
"name": "cart_btn_checkout", // 添加模块前缀
"value": "结算"
}
- 资源自动检查脚本:
bash复制#!/bin/bash
# 检测重复资源
find . -name "strings.json" | xargs grep -h '"name":' | sort | uniq -d
4. 开发效率优化技巧
4.1 模块独立调试
在module.json5中配置独立入口:
json复制{
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/features/cart/Main.ets",
"label": "$string:cart_app_name"
}
]
}
通过动态导入实现按需加载:
typescript复制// 动态加载商品详情模块
import('@feature-detail').then(module => {
module.startDetail(router, productId)
})
4.2 编译加速配置
在build-profile.json5中启用并行编译:
json复制"buildOption": {
"artifactType": "obfuscation",
"parallelCompile": true,
"cacheDir": "./build_cache"
}
实测效果:
- 全量编译:从3分12秒 → 1分58秒
- 增量编译:从46秒 → 22秒
5. 实战Demo解析
开源Demo包含以下核心模块:
code复制├── entry # 壳工程
├── feature-home # 首页
├── feature-cart # 购物车
├── library-payment # 支付SDK
└── common # 公共资源
关键代码片段:
typescript复制// 跨模块调用支付能力
import payment from '@library-payment'
@Builder
function CheckoutButton() {
Button('立即支付')
.onClick(() => {
payment.startPay({
amount: 99,
orderId: '123456'
})
})
}
运行效果:
- 首页模块独立运行:
bash复制hdc shell aa start -b com.example.featurehome
- 完整应用打包:
bash复制hvigor assembleRelease
6. 常见问题排查
6.1 模块找不到错误
现象:
code复制Error: Cannot find module '@feature-detail'
解决方案:
- 检查oh-package.json5依赖声明:
json复制"dependencies": {
"@feature-detail": "file:../feature-detail"
}
- 清理缓存后重试:
bash复制rm -rf oh_modules && npm install
6.2 资源ID冲突
现象:
code复制Resource @type/name=0x1000000 conflicts with...
解决步骤:
- 使用资源分析工具:
bash复制hdc shell aa dump -a
- 修改冲突的资源名:
diff复制- "name": "btn_confirm"
+ "name": "cart_btn_confirm"
7. 进阶优化方向
- 模块热更新方案:
typescript复制// 动态加载远程模块
import('https://cdn.example.com/module.js').then(...)
- 按需打包配置:
json复制"buildOption": {
"featureFlags": {
"enableCart": false // 构建时不打包购物车模块
}
}
- 模块健康度监控:
bash复制# 统计模块方法调用次数
hdc shell hiperf -n com.example.featurehome
这个改造过程中最深的体会是:模块化不是一蹴而就的,我们经历了三次架构调整。建议初期先做粗粒度拆分,随着业务复杂度的上升再逐步细化。当前Demo已上传至Gitee(搜索HarmonyModularDemo),包含完整的CI/CD配置和代码检查规则。
