
摘要
随着 HarmonyOS 在多设备、多场景中的应用越来越多,应用规模也在不断变大。从最早的单模块 App,到现在动辄十几个业务功能、多个团队协作,如果还用"一个模块写到底"的方式,维护成本会非常高。
模块化开发在鸿蒙中并不是一个"额外技巧",而是官方从架构层面就鼓励的一种开发方式。本文结合鸿蒙的模块体系,讲清楚模块该怎么拆、模块之间怎么通信,并通过可运行的 Demo 和真实业务场景,把模块化真正落地。
引言
在实际开发中,很多鸿蒙项目一开始都很简单,一个 entry 模块,页面、网络、工具类全堆在一起。但随着功能增加,很快就会遇到这些问题:
- 改一个功能,其他页面也跟着出问题
- 公共代码到处 copy,版本不一致
- 包体越来越大,启动越来越慢
- 多人协作时频繁冲突
鸿蒙本身提供了 Feature、Shared、Service 等模块形态,如果合理使用,可以很好地解决这些问题。接下来我们就从整体结构开始,一步一步拆解。
鸿蒙模块化开发的整体架构设计
模块角色划分思路
在一个标准的模块化鸿蒙项目中,每一类模块都有明确职责:
- Entry:应用入口,只负责调度和路由
- Feature:具体业务功能,一个模块一个业务
- Shared:公共能力,比如网络、工具类、基础 UI
- Service:后台服务或跨模块、跨设备能力
核心原则只有一句话:
业务只存在于 Feature,公共能力全部下沉到 Shared。
一个典型的模块化项目结构
text
MyHarmonyApp
├── entry
├── feature_login
├── feature_home
├── feature_user
├── shared_common
│ ├── http
│ ├── utils
│ └── ui
└── service_update
这个结构在实际项目中非常常见,后续新增功能,基本就是不断加 Feature 模块。
Entry 模块的职责与实现
Entry 只做调度,不写业务
Entry 模块是应用的入口,但很多新手会把业务页面直接写在 Entry 里,这样后期基本没法拆。
Entry 模块中只推荐做三件事:
- 应用启动逻辑
- 路由跳转
- Feature 模块调度
Entry 页面示例代码
ts
// entry/src/main/ets/pages/Index.ets
import router from '@ohos.router'
@Entry
@Component
struct Index {
build() {
Column() {
Button('进入登录页')
.onClick(() => {
router.pushUrl({
url: 'pages/LoginPage'
})
})
}
.width('100%')
.height('100%')
}
}
这里的 LoginPage 实际是定义在 feature_login 模块中的页面,只要 Entry 依赖了这个 Feature,就可以正常跳转。
Feature 模块的设计与实现
Feature 模块的基本原则
一个 Feature 模块只做一件事,比如:
- 登录功能
- 用户信息管理
- 订单管理
模块内部尽量做到自洽,不依赖其他 Feature。
Feature 登录模块示例
目录结构:
text
feature_login
└── src
└── main
└── ets
├── pages
│ └── LoginPage.ets
└── viewmodel
└── LoginViewModel.ts
登录页面代码示例
ts
// feature_login/pages/LoginPage.ets
import { LoginViewModel } from '../viewmodel/LoginViewModel'
@Component
struct LoginPage {
private vm: LoginViewModel = new LoginViewModel()
build() {
Column() {
TextInput({ placeholder: '用户名' })
.onChange(value => this.vm.username = value)
TextInput({ placeholder: '密码' })
.type(InputType.Password)
.onChange(value => this.vm.password = value)
Button('登录')
.onClick(() => {
this.vm.login()
})
}
.padding(20)
}
}
ViewModel 示例
ts
// feature_login/viewmodel/LoginViewModel.ts
import { HttpClient } from 'shared_common/http/HttpClient'
import { LogUtil } from 'shared_common/utils/LogUtil'
export class LoginViewModel {
username: string = ''
password: string = ''
async login() {
LogUtil.d('开始登录')
const result = await HttpClient.post('/login', {
username: this.username,
password: this.password
})
LogUtil.d(`登录结果:${JSON.stringify(result)}`)
}
}
这里可以看到,Feature 只关心业务逻辑,网络和日志能力全部来自 Shared。
Shared 模块的公共能力设计
Shared 模块适合放什么
Shared 模块中一般放这些内容:
- 网络请求封装
- 日志工具
- 本地存储
- 通用 UI 组件
不建议在 Shared 中放页面。
网络工具示例
ts
// shared_common/http/HttpClient.ts
export class HttpClient {
static async post(url: string, data: object) {
// 模拟网络请求
return new Promise(resolve => {
setTimeout(() => {
resolve({
code: 0,
data: {}
})
}, 500)
})
}
}
日志工具示例
ts
// shared_common/utils/LogUtil.ts
export class LogUtil {
static d(msg: string) {
console.info(`[AppLog] ${msg}`)
}
}
这些工具在所有 Feature 中都可以复用。
Service 模块在真实场景中的应用
为什么需要 Service 模块
当出现以下需求时,就很适合使用 Service:
- 后台任务
- OTA 升级
- 数据同步
- 跨模块能力调用
Service 可以让业务模块完全不关心实现细节。
OTA 升级 Service 示例
ts
// service_update/UpdateService.ts
import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility'
export default class UpdateService extends ServiceExtensionAbility {
onStart() {
console.info('OTA 升级服务启动')
}
}
Feature 中调用 Service
ts
import featureAbility from '@ohos.ability.featureAbility'
featureAbility.startAbility({
want: {
bundleName: 'com.example.app',
abilityName: 'UpdateService'
}
})
这样 Feature 只需要"使用升级能力",不关心升级是怎么实现的。
实际应用场景分析
场景一:大型业务 App 拆分
在一个包含登录、首页、用户中心、订单的 App 中:
- 每个业务一个 Feature
- 网络和存储统一放 Shared
- 登录态校验放 Service
这样新增业务只需要新增 Feature,不会影响已有功能。
场景二:智能设备远程升级
在你正在做的远程升级场景中:
- Service:负责下载、校验、重启
- Feature:提供升级 UI 和操作入口
- Shared:提供网络和文件工具
模块职责清晰,后期维护成本很低。
场景三:多人协作开发
模块化后,每个开发者负责一个 Feature:
- 冲突减少
- 测试更简单
- 发布更安全
常见问题 Q&A
Q1:Feature 之间可以直接依赖吗?
不推荐。如果确实需要共享能力,应该下沉到 Shared 或 Service。
Q2:所有功能都要拆 Feature 吗?
只要是相对独立的业务,都建议拆成 Feature,哪怕一开始规模不大。
Q3:模块多了会不会影响性能?
不会。鸿蒙支持 Feature 按需加载,合理拆分反而有利于性能优化。
总结
鸿蒙的模块化开发不是形式上的拆文件夹,而是一种清晰的架构思维:
- Entry 负责调度
- Feature 负责业务
- Shared 负责复用
- Service 负责能力输出
一旦项目按这个思路搭好,后续不管是功能扩展、团队协作,还是系统级能力接入,都会非常顺畅。