【HarmonyOS】鸿蒙应用模块化实现
本文基于 HarmonyOS 5.x + ArkTS + Stage模型,结合企业级商城、IM、金融、物联网等项目实战,详细讲解鸿蒙应用模块化设计思想。
第一章 为什么企业级HarmonyOS项目一定要模块化
一、什么是模块化(Modularization)
很多刚接触 HarmonyOS 的同学都会有一个误区:
把代码放到不同文件夹就是模块化。
实际上,并不是。
例如下面这种结构:
entry
├── pages
├── login
├── order
├── mine
├── cart
├── network
├── utils
└── model
虽然目录很多,但它们仍然属于 同一个 Module。
整个应用最终仍然会编译成:
Entry HAP
也就是说:
整个项目:
- 一个编译单元
- 一个依赖关系
- 一个生命周期
- 一个安装包
这并不能称之为真正的模块化。
什么才是真正的模块化?
真正的模块化,是把系统拆分成多个独立的模块(Module)。
例如:
App
├── entry
│
├── feature_login
│
├── feature_order
│
├── feature_cart
│
├── feature_mine
│
├── feature_home
│
├── feature_message
│
├── feature_pay
│
├── common_ui
│
├── common_network
│
├── common_router
│
├── common_database
│
├── common_account
│
├── common_permission
│
└── common_utils
每个模块:
- 独立开发
- 独立维护
- 独立编译
- 独立测试
- 可重复利用
最终共同组成整个 App。
这才是真正意义上的模块化。
二、为什么需要模块化?
假设现在开发一个商城 App。
项目刚开始的时候:
首页
登录
我的
整个项目可能只有:
10000 行代码
一个 Module 完全没有问题。
但是一年以后……
增加了:
订单
购物车
支付
直播
优惠券
搜索
消息
客服
定位
积分
会员
活动
秒杀
物流
发票
钱包
项目已经变成:
40 万行代码
继续开发:
80 万行
再继续:
120 万行
很多互联网公司的 HarmonyOS 项目:
200 万+
300 万+
500 万+
如果仍然只有一个 Module,会发生什么?
三、单 Module 项目的问题
问题一:编译越来越慢
例如:
entry
里面放了:
全部页面
全部组件
全部资源
全部网络
全部数据库
全部业务
任何地方修改了一行代码:
Text("Hello")
整个 Module 都需要重新参与编译。
例如:
修改首页按钮颜色
↓
重新编译整个项目
↓
等待几十秒甚至几分钟
项目越大:
编译速度越慢。
很多大型项目:
Clean Project
需要
5~10 分钟
对于开发效率来说,这是非常致命的。
问题二:多人开发冲突严重
假设:
团队有:
20 人
所有人都开发:
entry
Git 冲突会越来越严重。
例如:
A 修改:
routes.json
B 修改:
routes.json
C 修改:
routes.json
每天:
Git Merge
都会冲突。
尤其:
module.json5
build-profile.json5
oh-package.json5
这些公共配置文件。
几乎天天冲突。
问题三:代码耦合越来越严重
例如:
登录模块:
直接引用
订单页面
订单模块:
又引用
购物车
购物车:
又引用
首页
首页:
又引用
登录
形成:
Login
↓
Order
↓
Cart
↓
Home
↓
Login
这就是:
循环依赖(Circular Dependency)
后果:
编译异常
依赖混乱
维护困难
甚至出现:
无法打包
问题四:业务边界越来越模糊
例如:
登录相关代码:
login
↓
utils
↓
network
↓
pages
↓
common
↓
model
到处都是。
新人来了以后:
根本不知道:
登录代码到底在哪里。
通常会问:
登录接口在哪?
登录页面在哪?
登录缓存在哪?
登录 Token 在哪?
登录状态在哪?
找半天。
问题五:重复代码越来越多
例如:
订单模块:
自己写:
LoadingDialog
首页:
又写一个:
LoadingDialog
购物车:
再写一个:
LoadingDialog
消息模块:
继续写:
LoadingDialog
最后:
整个项目:
20 个 Loading
功能一样。
代码不同。
维护十分痛苦。
问题六:资源越来越混乱
例如:
图片:
icon_back.png
结果:
首页:
一个
订单:
一个
购物车:
一个
支付:
一个
实际上:
完全一样。
最后:
安装包越来越大。
问题七:启动越来越慢
例如:
首页其实只需要:
首页
登录
网络
但是由于:
所有代码:
全部打包进:
Entry
启动时:
所有初始化:
数据库
推送
地图
IM
支付
定位
蓝牙
AI
全部执行。
启动时间越来越长。
四、模块化能够解决什么?
模块化最大的目标只有一句话:
高内聚,低耦合。
什么意思?
例如:
登录模块:
feature_login
里面只有:
登录页面
登录接口
Token
用户信息
验证码
密码登录
短信登录
不会引用:
购物车
订单
支付
订单模块:
feature_order
里面:
只有:
订单
退款
售后
评价
这就是:
高内聚。
不同模块之间:
通过接口通信。
而不是:
直接引用页面。
这就是:
低耦合。
五、模块化带来的收益
真正落地模块化以后,一个大型 HarmonyOS 项目通常会得到以下收益:
| 收益 | 说明 |
|---|---|
| 编译更快 | 修改局部模块即可重新编译 |
| 开发效率更高 | 各团队独立开发,互不影响 |
| Git 冲突减少 | 每个团队维护自己的模块 |
| 代码职责清晰 | 模块边界明确,易于维护 |
| 更容易测试 | 单模块可独立调试、单元测试 |
| 更容易复用 | 公共能力可沉淀为 HAR/HSP |
| 安装包优化 | 支持按需加载,减少初始包体积 |
| 更利于持续迭代 | 新业务可新增模块,不影响现有功能 |
六、模块化不是拆得越细越好
很多团队一开始会陷入另一个误区:
Button
↓
一个 Module
Text
↓
一个 Module
Image
↓
一个 Module
结果:
项目变成:
80 多个 Module
依赖关系复杂到无法维护。
因此:
模块化的目标不是“越多越好”,而是“职责清晰、粒度合理”。
通常建议:
- 按业务拆分:登录、订单、支付、消息等。
- 按公共能力拆分:网络、数据库、工具、公共 UI。
- 避免为了拆分而拆分,否则维护成本会高于收益。
本章小结
本章主要介绍了 HarmonyOS 模块化的必要性,以及单 Module 项目在企业开发中的常见问题。理解这些背景后,后续学习 HAR、HSP、Feature HAP、Entry HAP 等模块类型,以及企业级模块架构设计会更加容易。
第二章 HarmonyOS 四种模块(Entry、Feature、HAR、HSP)深度解析
上一章,我们介绍了为什么企业项目一定要做模块化。
但是很多开发者真正开始拆分项目时,又会遇到新的问题:
HarmonyOS 为什么有四种 Module?
例如:
Entry HAP
Feature HAP
HAR
HSP
很多人刚开始学习的时候,经常分不清:
- HAR 和 HSP 有什么区别?
- 为什么官方现在推荐 HSP?
- Feature 为什么还能独立安装?
- 公共代码到底应该放 HAR 还是 HSP?
- 企业项目到底如何选择?
这一章,我们就彻底讲透 HarmonyOS 四种模块。
一、HarmonyOS 的模块组成
一个完整的 HarmonyOS 应用,并不是只有一个 Module。
官方推荐采用:
App
│
├── Entry HAP
│
├── Feature HAP
│
├── HAR
│
└── HSP
可以把它理解成一个大型建筑:
整个App
↓
一栋写字楼
里面包含很多楼层。
例如:
Entry
↓
大厅
Feature
↓
各个办公区
HAR
↓
公共工具室
HSP
↓
共享会议室
每一种 Module 都承担不同职责。
二、什么是 HAP?
很多新人第一眼看到:
Entry HAP
就会问:
HAP 是什么?
其实:
HarmonyOS 的安装包就是:
HAP
类似于 Android:
APK
或者:
iOS:
IPA
因此:
Entry
↓
最终生成
↓
Entry.hap
Feature:
Feature
↓
Feature.hap
都属于:
Harmony Ability Package
即:
HarmonyOS Ability 安装包。
三、Entry HAP
Entry 是整个 App 的入口模块。
例如:
entry
通常里面包括:
App启动
Ability
首页
Navigation
初始化SDK
启动配置
项目结构:
entry
│
├── src
│
├── App.ets
│
├── EntryAbility
│
├── resources
│
└── module.json5
整个应用:
安装后:
首先启动:
Entry Ability
因此:
Entry:
一定存在。
一个应用:
通常:
只有一个 Entry
Entry 的职责
Entry 不应该写大量业务。
它应该负责:
App启动
初始化
首页
路由入口
Ability
启动页
全局配置
例如:
AppStorage.setOrCreate("token", "")
例如:
初始化:
Logger.init()
Router.init()
Network.init()
Database.init()
这些都适合放 Entry。
但是:
不要把:
订单
支付
聊天
商城
直播
全部放进去。
否则:
Entry 会越来越庞大。
四、Feature HAP
Feature:
就是:
业务模块。
例如:
商城:
feature_home
订单:
feature_order
支付:
feature_pay
聊天:
feature_chat
个人中心:
feature_mine
整个项目:
可能变成:
App
│
├── entry
│
├── feature_home
│
├── feature_order
│
├── feature_pay
│
├── feature_message
│
└── feature_mine
是不是业务边界一下子清晰了?
五、Feature 的优点
例如:
订单团队:
只维护:
feature_order
聊天团队:
维护:
feature_chat
首页团队:
维护:
feature_home
互不影响。
Git:
冲突明显减少。
Feature 可以独立编译
例如:
修改:
feature_order
理论上:
只需要重新编译:
feature_order
而不是:
整个工程。
对于大型项目:
开发效率会提升很多。
Feature 可以按需安装
HarmonyOS 一个非常大的优势就是:
Feature 支持:
按需安装(On Demand Install)。
例如:
用户第一次安装:
只安装:
Entry
Home
Mine
支付:
暂时不安装。
直播:
暂时不安装。
AI:
暂时不安装。
直到:
用户点击:
直播
系统:
再下载:
feature_live.hap
这样:
首包:
就可以非常小。
六、HAR(Harmony Archive)
很多人第一次看到:
HAR
以为:
也是一个 App。
其实不是。
HAR:
不能安装。
不能运行。
不能启动。
它只是:
一个代码库。
例如:
common_utils
里面:
DateUtil
StringUtil
FileUtil
ToastUtil
SPUtil
最终:
编译成:
common_utils.har
其它 Module:
依赖:
common_utils.har
即可。
HAR 的特点
HAR:
最大的特点:
不能独立安装
例如:
common_network.har
里面:
HttpClient
Request
Response
Interceptor
不会:
生成:
HAP
而是:
被:
Entry
Feature
引用。
编译时:
代码:
直接:
复制进去。
因此:
HAR:
类似:
Android:
AAR
或者:
Java:
Jar
七、HAR 的优缺点
优点:
简单
方便
依赖少
容易开发
但是:
它也有一个最大的问题:
代码会复制。
例如:
Entry
↓
引用
common.har
订单:
Order
↓
引用
common.har
支付:
Pay
↓
引用
common.har
最终:
编译后:
Entry
包含 common
Order
也包含 common
Pay
再次包含 common
也就是说:
同一份代码:
可能:
出现:
很多次。
对于大型项目:
包体积:
会越来越大。
八、HSP(Harmony Shared Package)
HSP:
官方近几年重点推荐的共享模块方案。
可以理解为:
真正的共享运行时模块。
例如:
common_network
整个 App:
只有:
一个
实例。
所有 Feature:
共同使用。
而不是:
复制很多份。
例如:
Feature_A
↓
HSP
Feature_B
↓
HSP
Feature_C
↓
HSP
大家:
共享:
同一份:
代码。
因此:
安装包:
明显变小。
九、HAR 与 HSP 的本质区别
下面这张表,是面试中最常见的问题之一:
| 对比项 | HAR | HSP |
|---|---|---|
| 是否可独立安装 | ❌ | ✅(作为共享包随应用分发) |
| 是否可运行 | ❌ | ❌ |
| 是否支持共享 | 编译时复制 | 运行时共享 |
| 是否会重复打包 | 会 | 不会 |
| 是否支持按需加载 | ❌ | ✅ |
| 推荐场景 | 小型公共库 | 企业级公共能力 |
一句话总结:
- HAR:编译时共享,代码会复制到依赖它的模块中。
- HSP:运行时共享,一份代码供多个模块共同使用,更适合大型项目。
十、企业项目如何选择?
一般建议如下:
Entry
负责:
App启动
Ability
Navigation
初始化
Feature
负责:
登录
首页
订单
支付
聊天
个人中心
HAR
适合:
工具类
字符串处理
日期工具
日志工具
简单UI组件
HSP
适合:
网络
数据库
账号体系
IM
公共SDK
权限管理
Router
公共业务能力
这些模块通常会被多个 Feature 同时依赖,采用 HSP 能避免重复打包,提高运行效率。
十一、一个企业级项目的模块示例
App
│
├── entry // 应用入口
│
├── feature_home // 首页
├── feature_login // 登录
├── feature_order // 订单
├── feature_cart // 购物车
├── feature_pay // 支付
├── feature_message // 消息
├── feature_mine // 我的
│
├── common_ui // UI组件(HAR)
├── common_utils // 工具(HAR)
│
├── core_network // 网络(HSP)
├── core_database // 数据库(HSP)
├── core_account // 用户(HSP)
├── core_router // 路由(HSP)
├── core_permission // 权限(HSP)
└── core_base // 基础能力(HSP)
这种结构具有以下特点:
- 业务模块职责单一,便于团队协作。
- 公共能力统一维护,避免重复实现。
- 后续新增业务(如直播、AI、会员)时,只需新增对应 Feature,不影响已有模块。
本章小结
本章重点介绍了 HarmonyOS 四种模块类型及其定位:
- Entry HAP:应用入口,负责启动和全局初始化。
- Feature HAP:承载具体业务功能,可按需安装。
- HAR:静态共享代码库,适合工具和简单公共组件。
- HSP:运行时共享包,适合网络、数据库、账号等核心公共能力,是企业级项目的推荐方案。
第三章 企业级模块划分原则(按业务、按能力、按层次)
上一章,我们已经了解了:
- Entry
- Feature
- HAR
- HSP
很多同学觉得:
"我已经知道四种 Module 了,现在是不是直接拆模块就行?"
实际上,真正困难的并不是创建 Module。
而是:
如何划分 Module。
很多公司的项目:
Module越来越多,
最后却越来越乱。
例如:
common
common2
common3
base
base2
utils
utils2
sdk
core
business
manager
module
半年以后……
连项目负责人都不知道:
某个工具类到底放哪里。
所以,
企业真正需要的是:
模块划分原则(Module Design Principle)
这一章,我们从大型互联网项目出发,讲透HarmonyOS模块拆分思想。
一、模块不是拆得越多越好
很多新人最容易犯的错误:
看到官方推荐模块化,
于是开始疯狂创建Module。
例如:
Button
↓
button_module
Text:
text_module
Image:
image_module
Toast:
toast_module
Dialog:
dialog_module
最后:
整个项目:
90+
Module
结果:
依赖关系变成:
Button
↓
Toast
↓
Utils
↓
Base
↓
Common
↓
Network
↓
Button
形成:
循环依赖。
整个项目:
没人敢动。
为什么不能拆太细?
因为:
Module:
不是目录。
它具有:
独立编译
独立依赖
独立资源
独立生命周期
Module越多:
意味着:
更多依赖
更多配置
更多版本
更多维护成本
所以:
模块数量:
并不是越多越先进。
而是:
刚刚好。
二、企业为什么拆模块?
真正的大厂:
拆模块:
只有三个目的:
职责分离
代码复用
团队协作
除此之外:
不要拆。
例如:
商城:
首页
订单
购物车
支付
消息
个人中心
它们:
天然就是:
不同业务。
所以:
拆:
feature_home
feature_order
feature_cart
没有任何问题。
但是:
不要:
OrderList
↓
一个Module
OrderDetail:
一个Module
OrderRefund:
一个Module
这种拆法:
维护成本:
远远高于收益。
三、企业级模块划分三原则
真正的大型HarmonyOS项目,
通常遵循:
第一原则:按业务拆
这是:
最推荐的方式。
例如:
feature_login
负责:
登录
注册
验证码
找回密码
短信登录
全部:
属于:
登录业务。
不要:
把:
验证码:
放:
common
密码:
放:
utils
登录:
放:
pages
这样:
业务就散了。
应该:
全部:
放:
feature_login
这叫:
业务内聚。
再例如:
订单:
feature_order
里面:
订单列表
订单详情
退款
售后
评价
物流
订单支付
全部:
属于:
订单。
放一起。
新人:
一分钟:
就能找到。
第二原则:按能力拆
有一些东西:
并不是:
业务。
例如:
网络:
HttpClient
数据库:
Database
日志:
Logger
Toast:
Toast
Router:
Router
权限:
Permission
它们:
所有业务:
都会使用。
所以:
应该:
抽出来。
例如:
core_network
里面:
Request
Response
Interceptor
HttpClient
Retry
订单:
调用:
http.get()
首页:
调用:
http.get()
登录:
也是:
http.get()
大家:
共享。
这就是:
能力模块。
第三原则:按层次拆
企业项目:
一般:
至少:
四层。
例如:
UI
↓
业务
↓
数据
↓
基础能力
可以画成:
Feature
↓
Repository
↓
Network
↓
Database
所有:
Feature:
只能:
调用:
Repository。
Repository:
负责:
获取:
数据。
Repository:
调用:
Network。
Network:
调用:
Http。
这样:
职责:
非常清晰。
四、一个正确的企业模块结构
例如:
商城:
App
│
├── entry
│
├── feature_home
├── feature_login
├── feature_order
├── feature_cart
├── feature_message
├── feature_pay
├── feature_mine
│
├── core_network
├── core_database
├── core_router
├── core_account
├── core_permission
├── core_storage
│
├── common_ui
├── common_utils
├── common_widget
│
└── model
是不是:
职责:
已经非常清晰?
五、为什么很多公司都有 Base 模块?
很多HarmonyOS项目:
都会有:
base
很多新人:
不知道:
Base:
到底放什么?
其实:
Base:
不是:
工具类。
也不是:
公共组件。
Base:
应该放:
整个应用:
最基础的东西。
例如:
BaseAbility
BaseViewModel
BaseRepository
BasePage
BaseDialog
BaseConstants
BaseException
这些:
所有模块:
都会继承。
例如:
export class HomeViewModel extends BaseViewModel {
}
订单:
也是:
export class OrderViewModel extends BaseViewModel {
}
统一:
生命周期。
统一:
日志。
统一:
异常。
统一:
Loading。
六、为什么还有 Core?
很多项目:
又有:
core
Base:
和:
Core:
有什么区别?
很多人:
混了。
其实:
很好理解。
Base:
偏:
抽象。
例如:
BaseViewModel
Core:
偏:
能力。
例如:
core_network
提供:
HttpClient
Core:
不是:
父类。
而是:
能力。
例如:
core_database
里面:
Room
Preferences
KVStore
都是:
能力。
七、为什么还有 Common?
很多项目:
还有:
common
Common:
放什么?
一般:
放:
可以直接使用的:
公共资源。
例如:
Loading
Dialog
TitleBar
EmptyView
ErrorView
NoDataView
RefreshLayout
大家:
直接:
引用。
即可。
八、Feature 为什么不能互相引用?
例如:
订单:
引用:
feature_login
登录:
又引用:
feature_order
是不是:
很容易:
形成:
Order
↓
Login
↓
Order
循环依赖。
因此:
企业:
一般:
规定:
Feature
不能依赖
Feature
那怎么办?
抽:
公共模块。
例如:
UserRepository
放:
core_account
登录:
调用:
UserRepository.login()
订单:
调用:
UserRepository.getUser()
大家:
共同:
依赖:
core_account
而不是:
互相引用。
九、模块依赖应该像一棵树
企业:
推荐:
依赖:
单向。
例如:
Entry
│
┌────────────┼────────────┐
│ │ │
FeatureHome FeatureOrder FeatureMine
│ │ │
└────────────┼────────────┘
│
Repository(HSP)
│
┌───────────┼────────────┐
│ │ │
Network(HSP) Database(HSP) Account(HSP)
│
│
Utils(HAR)
注意:
所有箭头:
只有:
向下。
绝不能:
反向。
十、一个错误的依赖关系
例如:
FeatureHome
↓
FeatureOrder
↓
FeaturePay
↓
FeatureMine
↓
FeatureHome
最终:
形成:
Circle
编译:
越来越慢。
维护:
越来越困难。
所以:
企业:
一般:
使用:
单向依赖(One-way Dependency)。
十一、模块划分的黄金法则
如果一个模块满足下面三个条件中的两个以上,就应该考虑拆分:
| 判断标准 | 是否建议拆分 |
|---|---|
| 功能可以独立开发 | ✅ |
| 可以独立测试 | ✅ |
| 可以独立发布或升级 | ✅ |
| 被多个业务共同依赖 | ✅ |
| 团队独立维护 | ✅ |
反之,如果一个模块:
- 只能被一个页面使用;
- 没有复用价值;
- 拆出来反而增加依赖;
那么通常没有必要单独建 Module。
十二、企业项目模块命名规范
建议统一命名,避免出现 common2、newCommon 这类难以维护的名称。
entry // 应用入口
feature_login // 登录业务
feature_home // 首页业务
feature_order // 订单业务
feature_cart // 购物车业务
feature_pay // 支付业务
feature_message // 消息业务
feature_mine // 我的业务
core_network // 网络核心能力
core_database // 数据库能力
core_account // 账号能力
core_router // 路由能力
core_permission // 权限能力
core_storage // 存储能力
common_ui // 公共UI组件
common_utils // 工具库
common_widget // 通用业务组件
base // 基础抽象层
这样的命名,即使是新加入团队的开发者,也能快速理解整个工程结构。
本章总结
本章围绕 HarmonyOS 企业级模块划分原则 展开,重点掌握了:
- 为什么模块不是越多越好;
- 三大拆分原则:按业务、按能力、按层次;
- Base、Core、Common 的职责边界;
- 为什么 Feature 不应直接依赖 Feature;
- 如何设计单向依赖,避免循环引用;
- 企业项目推荐的模块命名规范与目录结构。
这些原则不仅适用于 HarmonyOS,也适用于 Android、Flutter 等大型客户端项目。
第四章 HarmonyOS Module 创建与配置(Entry、Feature、HAR、HSP)实战
前面三章,我们已经理解了模块化设计思想。
但是很多开发者真正开始搭建项目时,又会遇到新的问题:
DevEco Studio 到底如何创建 Module?
例如:
- Entry 怎么创建?
- Feature 如何依赖 Entry?
- HAR 怎么导出接口?
- HSP 怎么共享?
module.json5到底有什么作用?build-profile.json5怎么配置?oh-package.json5怎么管理依赖?
这一章,我们通过完整实战,把整个过程讲透。
一、HarmonyOS 工程结构
创建一个默认 HarmonyOS 工程后,大概如下:
Demo
│
├── AppScope
│
├── entry
│
│ ├── src
│ ├── resources
│ ├── module.json5
│ └── oh-package.json5
│
├── build-profile.json5
│
├── hvigorfile.ts
│
├── hvigor
│
└── oh-package.json5
很多新人第一眼看到:
AppScope
就蒙了。
实际上:
整个工程可以理解成三层:
工程(Project)
↓
Module
↓
Source
即:
Project
│
├── Entry Module
│
└── Feature Module
二、什么是 Project?
Project:
就是整个应用。
例如:
ShoppingMall
里面可以拥有:
Entry
Feature
HAR
HSP
所有 Module。
因此:
一个 Project:
可以包含:
几十个 Module。
例如:
Project
↓
35 Modules
企业项目:
十分常见。
三、Module 创建流程
在 DevEco Studio 中:
右键:
Project
选择:
New
↓
Module
随后会看到:
Entry
Feature
Static Library(HAR)
Shared Library(HSP)
不同版本 DevEco Studio 的名称可能略有差异,但核心类型保持一致。
四、创建 Entry
通常:
工程创建时:
已经自动生成:
entry
目录:
entry
│
├── src
│
├── resources
│
├── module.json5
│
├── oh-package.json5
│
└── build-profile.json5
一般:
不要删除。
整个App:
都会从这里启动。
五、创建 Feature
例如:
创建:
feature_order
目录:
feature_order
│
├── src
│
├── resources
│
├── module.json5
│
└── oh-package.json5
Feature:
里面:
可以写:
OrderPage
OrderDetail
OrderViewModel
OrderRepository
它:
就是:
订单业务。
一个业务一个 Feature
例如:
feature_login
负责:
登录
注册
验证码
feature_cart
负责:
购物车
商品结算
feature_message
负责:
聊天
通知
消息中心
这样:
职责:
十分清晰。
六、创建 HAR
选择:
Static Library
即可。
例如:
common_utils
生成:
common_utils
│
├── src
│
├── Index.ets
│
└── oh-package.json5
注意:
HAR:
没有:
Ability
没有:
pages
因为:
它不能运行。
七、HAR 为什么有 Index.ets?
例如:
里面:
// DateUtil.ets
export class DateUtil {
static now(): string {
return "2026"
}
}
但是:
外部:
不能:
直接:
引用。
需要:
统一:
导出。
例如:
// Index.ets
export * from './src/main/ets/utils/DateUtil'
以后:
其它Module:
只需要:
import { DateUtil } from 'common_utils'
即可。
这就是:
统一出口。
也是:
企业:
推荐:
做法。
八、创建 HSP
选择:
Shared Library
例如:
core_network
生成:
core_network
│
├── src
│
├── Index.ets
│
├── module.json5
│
└── oh-package.json5
HSP:
和:
HAR:
目录:
几乎一样。
最大的区别:
不是:
目录。
而是:
编译方式。
九、Module 的配置文件
几乎:
每个Module:
都有:
module.json5
很多新人:
不知道:
它到底干嘛。
实际上:
它就是:
Module:
身份证。
例如:
{
"module": {
"name": "feature_order",
"type": "feature",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
]
}
}
里面:
记录:
Module名称
类型
Ability
设备
权限
等等。
十、module.json5 常见字段
name
"name": "feature_order"
模块名称。
也是:
依赖名称。
type
例如:
"type":"entry"
或者:
"type":"feature"
决定:
当前:
Module:
类型。
mainElement
例如:
"mainElement":"EntryAbility"
指定:
启动Ability。
通常:
只有:
Entry:
需要。
deviceTypes
例如:
[
"phone",
"tablet"
]
表示:
当前模块:
支持:
哪些设备。
例如:
还能支持:
TV
Wearable
Car
十一、build-profile.json5
很多新人:
第一次:
看到:
build-profile.json5
以为:
只是:
Gradle。
其实:
不是。
它:
负责:
整个:
工程:
编译配置。
例如:
{
"apiType":"stageMode"
}
还包括:
Signing
Product
BuildMode
Targets
等等。
十二、oh-package.json5
这是:
鸿蒙:
依赖管理文件。
类似:
Android:
build.gradle
或者:
Node:
package.json
例如:
{
"dependencies": {
}
}
所有:
第三方:
SDK:
都:
配置:
这里。
例如:
axios
crypto
二维码
图片库
等等。
十三、多 Module 如何依赖?
例如:
订单:
需要:
工具类。
首先:
common_utils:
导出:
export * from './DateUtil'
然后:
Order:
依赖:
{
"dependencies": {
"common_utils":"file:../common_utils"
}
}
随后:
即可:
import { DateUtil } from 'common_utils'
是不是:
和:
npm:
很像?
十四、Feature 如何依赖 HSP?
例如:
网络:
放:
core_network
订单:
直接:
依赖:
{
"dependencies": {
"core_network":"file:../core_network"
}
}
然后:
import { HttpClient } from 'core_network'
所有:
Feature:
都:
可以:
共享。
十五、一个企业项目依赖关系
例如:
entry
│
┌─────────┴──────────┐
│ │ │
feature_home feature_order feature_mine
│ │ │
└─────────┼──────────┘
│
core_network(HSP)
│
core_database(HSP)
│
common_utils(HAR)
这里:
要注意:
所有依赖:
都:
向下。
不要:
反向。
十六、统一导出 Index.ets
企业:
都会:
要求:
每个Module:
都有:
Index.ets
例如:
export * from './network/HttpClient'
export * from './network/Api'
export * from './network/Interceptor'
export * from './repository/UserRepository'
export * from './repository/OrderRepository'
以后:
外部:
只需要:
import {
HttpClient,
Api,
UserRepository
} from 'core_network'
而不是:
import { HttpClient } from '../../../../../network'
这样:
路径:
不会:
越来越长。
十七、推荐目录结构
企业推荐每个 Module 保持统一目录风格,例如:
feature_order
│
├── src
│ └── main
│ ├── ets
│ │ ├── pages
│ │ ├── components
│ │ ├── viewmodel
│ │ ├── repository
│ │ ├── model
│ │ ├── service
│ │ ├── constants
│ │ └── utils
│ │
│ └── resources
│
├── Index.ets
├── module.json5
└── oh-package.json5
统一的目录规范可以让团队成员在不同业务模块之间快速切换,降低沟通成本。
十八、模块创建中的常见问题
1. 一个 Feature 可以直接引用另一个 Feature 吗?
不建议。
正确做法是:
- 抽取公共能力到 HSP;
- 或抽取公共工具到 HAR;
- Feature 之间通过公共接口通信,而不是直接依赖。
2. HAR 可以包含页面吗?
可以包含页面代码,但 不能作为独立应用运行,通常不建议将完整业务页面放入 HAR。HAR 更适合作为工具库、UI 组件库等公共能力。
3. HSP 能否保存单例状态?
可以。由于 HSP 在运行时是共享的,非常适合维护全局账号信息、网络管理器、数据库连接等公共状态。但也要注意线程安全和生命周期管理。
本章总结
本章进入了 HarmonyOS 模块化的实战阶段,介绍了:
- 如何创建 Entry、Feature、HAR、HSP;
module.json5、build-profile.json5、oh-package.json5的作用;- Module 之间的依赖关系配置;
Index.ets的统一导出规范;- 企业项目推荐的目录结构与实践建议。
掌握这些内容后,就可以真正开始搭建一个多 Module 的 HarmonyOS 工程。
第五章 企业级模块依赖设计(避免循环依赖)
在前面章节中,我们已经完成了:
- 模块分类设计
- Module 创建
- HAR/HSP 使用方式
- 基础目录规划
但是,当项目真正扩大以后,会遇到一个非常棘手的问题:
模块越来越多,依赖关系越来越复杂,最后出现循环依赖。
这是企业级 HarmonyOS 项目中最常见、也是最难解决的问题之一。
一、什么是模块依赖?
假设:
我们有:
feature_order
订单模块。
需要:
core_network
网络能力。
那么:
依赖关系:
feature_order
↓
core_network
表示:
订单模块:
可以使用:
网络模块。
代码:
import {
HttpClient
} from "core_network"
这就是:
模块依赖。
二、什么是正确的依赖方向?
企业项目:
最核心原则:
高层业务依赖底层能力,底层不能反向依赖业务。
例如:
正确:
Entry
↓
Feature业务层
↓
Repository数据层
↓
Core能力层
↓
Utils基础层
依赖方向:
永远:
向下。
三、错误的依赖设计
很多初学者会这样设计:
登录模块
feature_login
里面:
LoginPage
LoginViewModel
然后:
订单:
需要用户信息。
于是:
订单引用登录:
feature_order
↓
feature_login
看起来没问题。
但是:
登录退出时:
又需要通知订单刷新:
于是:
登录又引用订单:
feature_login
↓
feature_order
最终:
变成:
feature_login
↓
feature_order
↓
feature_login
形成:
循环依赖。
四、循环依赖有什么危害?
1. 编译失败
例如:
A
依赖
B
B
依赖
A
编译系统无法确定:
先编译谁。
2. 模块职责混乱
例如:
登录模块:
开始出现:
OrderList
OrderDetail
OrderRepository
订单模块:
出现:
LoginManager
UserLogin
最后:
没人知道:
代码应该放哪里。
3. 后期无法拆分
未来:
想把:
订单业务:
独立出去。
发现:
订单:
依赖登录。
登录:
依赖订单。
根本拆不出来。
五、如何解决循环依赖?
企业常用:
五种方式。
方案一:抽取公共能力模块
这是:
最常见方式。
错误:
订单
↓
登录
原因:
订单需要:
用户信息。
那么:
不要引用登录。
抽出来:
core_account
结构:
feature_login
↓
core_account
feature_order
↓
core_account
例如:
账号模块:
core_account
提供:
export class AccountManager {
getUserInfo(){
}
getToken(){
}
}
登录:
调用:
AccountManager.login()
订单:
调用:
AccountManager.getUserInfo()
两个业务:
互不依赖。
六、方案二:接口隔离
假设:
订单需要:
支付能力。
不要:
直接依赖:
feature_pay
例如:
错误:
import {
PayPage
}
from "feature_pay"
因为:
业务模块耦合。
正确:
抽象接口:
core_payment
定义:
export interface PaymentService {
pay(
orderId:string
):Promise<boolean>
}
支付模块实现:
class WxPaymentService
implements PaymentService {
}
订单:
只依赖:
接口。
不知道:
具体实现。
结构:
feature_order
↓
core_payment
↑
feature_pay
依赖:
变成:
单方向。
七、方案三:事件通信
很多时候:
两个业务:
只是需要:
通知。
例如:
登录成功:
通知首页刷新。
错误:
首页:
直接引用:
登录。
home
↓
login
正确:
使用:
EventHub。
例如:
登录:
发送:
eventHub.emit(
"LOGIN_SUCCESS"
)
首页:
监听:
eventHub.on(
"LOGIN_SUCCESS",
()=>{
refresh()
}
)
结构:
变成:
login
↓
EventCenter
↑
home
两个模块:
没有直接依赖。
八、方案四:Router解耦页面跳转
很多项目:
最大的问题:
就是:
页面跳转。
例如:
订单:
跳登录。
代码:
router.pushUrl({
url:"LoginPage"
})
这样:
订单:
知道:
登录页面存在。
产生:
耦合。
正确:
统一:
Router模块。
例如:
core_router
定义:
Router.open(
"login"
)
订单:
只知道:
路由名称。
不知道:
页面在哪里。
结构:
feature_order
↓
core_router
↑
feature_login
九、方案五:Repository层隔离
这是:
大型项目:
最常用架构。
例如:
订单页面:
不要:
直接访问:
网络。
错误:
OrderPage
↓
HttpClient
正确:
增加:
Repository:
OrderPage
↓
OrderRepository
↓
Network
结构:
feature_order
↓
repository
↓
core_network
例如:
页面:
orderRepository.getOrders()
Repository:
内部:
http.get("/orders")
页面:
完全不知道:
接口细节。
十、企业推荐依赖模型
大型 HarmonyOS 项目:
推荐:
下面结构:
entry
↓
-------------------
feature层
↓
-------------------
repository层
↓
-------------------
core能力层
↓
-------------------
base/utils层
具体:
entry
↓
feature_login
feature_home
feature_order
feature_pay
↓
core_account
core_router
core_network
core_database
↓
common_utils
common_ui
↓
base
十一、哪些模块可以互相依赖?
整理一个规则:
| 模块 | 可以依赖 |
|---|---|
| Entry | Feature、Core、Common |
| Feature | Core、Common、Base |
| Core | Base、Common |
| Common | Base |
| Base | 无 |
重点:
Feature:
不要依赖:
Feature。
十二、企业项目依赖检查规则
可以制定:
代码规范:
例如:
禁止:
feature_x
↓
feature_y
禁止:
core
↓
feature
禁止:
utils
↓
network
因为:
工具层:
不应该知道:
业务。
十三、实际商城项目依赖示例
完整结构:
entry
|
-----------------------------------
| | |
feature_home feature_order feature_pay
| | |
-----------------------------------
|
core_repository
|
-----------------------------------
| | |
core_network core_database core_account
|
common_utils
|
base
这个结构:
可以支持:
百万级代码规模。
十四、模块化设计口诀
企业开发中:
总结为:
业务向下依赖,基础禁止向上。
Feature不互相引用,公共能力统一沉淀。
页面不要调接口,Repository隔离业务。
模块之间少直接调用,多使用接口和事件。
十五、本章总结
本章重点:
1. 理解模块依赖
模块之间:
通过依赖关系:
连接。
2. 避免循环依赖
不要:
Feature依赖Feature。
3. 使用解耦方案
包括:
- 公共能力抽取
- 接口隔离
- EventHub通信
- Router跳转
- Repository架构
4. 掌握企业依赖方向
最终目标:
形成:
业务层
↓
能力层
↓
基础层
单向依赖。
第六章 HarmonyOS 模块化架构中的 MVVM 设计
前面五章,我们已经完成了:
- 为什么需要模块化;
- Entry / Feature / HAR / HSP 的区别;
- 企业模块拆分原则;
- Module 依赖设计;
- 如何避免循环依赖。
但是还有一个核心问题:
一个 Feature Module 内部,代码应该怎么组织?
例如:
feature_order
里面:
到底放:
- 页面?
- 网络请求?
- 数据模型?
- 状态管理?
- 数据库?
如果没有规范,一个 Feature 很快又会变成:
feature_order
├── pages
├── api
├── utils
├── model
├── manager
├── service
├── network
├── database
└── xxx
最后:
模块化失败。
因此企业项目通常会在 Module 内部继续采用:
MVVM + Repository 分层架构
一、为什么模块化需要 MVVM?
模块化解决的是:
大模块之间的问题。
MVVM解决的是:
一个模块内部的问题。
两者关系:
如下:
App模块化
解决:
模块边界
MVVM
解决:
模块内部代码职责
例如:
整个App:
feature_order
负责订单。
但是内部:
需要:
订单页面
订单状态
订单数据
订单接口
订单缓存
这时候:
使用:
MVVM。
二、HarmonyOS MVVM整体结构
企业项目常见:
如下:
Feature
│
├── View
│
├── ViewModel
│
├── Repository
│
├── DataSource
│
└── Model
完整链路:
用户操作
↓
View
↓
ViewModel
↓
Repository
↓
Network / Database
↓
Server
三、View层设计
View:
就是:
ArkUI页面。
例如:
订单列表:
pages
OrderListPage.ets
负责:
显示UI。
例如:
@Component
export struct OrderListPage {
build(){
List(){
}
}
}
View:
只负责:
三个事情:
1. 展示数据
例如:
Text(
this.vm.orderCount.toString()
)
2. 响应用户操作
例如:
点击:
Button("刷新")
.onClick(()=>{
this.vm.refresh()
})
3. 页面布局
例如:
Column(){
Text()
List()
Button()
}
View:
不要:
做:
网络请求。
错误:
.onClick(()=>{
http.get()
})
为什么?
因为:
UI知道业务细节。
后期:
无法维护。
四、ViewModel层设计
ViewModel:
是:
View和业务之间的桥梁。
负责:
- 状态管理;
- 调用业务;
- 处理页面逻辑。
例如:
export class OrderViewModel {
orders:Order[]=[]
async loadOrders(){
}
}
页面:
只调用:
vm.loadOrders()
五、HarmonyOS状态管理结合ViewModel
HarmonyOS:
常用:
状态:
包括:
@State
@Observed
@ObjectLink
@Provide
@Consume
企业项目:
通常:
不会:
让页面承担全部状态。
例如:
错误:
@State orders=[]
全部写页面。
页面:
越来越大。
推荐:
ViewModel管理:
class OrderViewModel {
@State
orders=[]
}
页面:
绑定:
this.vm.orders
六、Repository层设计
Repository:
是:
MVVM核心。
它负责:
统一数据来源。
例如:
订单数据:
可能来自:
服务器:
HTTP
本地:
Database
缓存:
Preferences
页面:
不应该知道。
例如:
页面:
调用:
orderVM.loadOrders()
ViewModel:
调用:
repository.getOrders()
Repository:
决定:
去哪取。
结构:
OrderPage
↓
OrderViewModel
↓
OrderRepository
↓
----------------
HTTP
Database
Cache
----------------
七、为什么一定需要Repository?
假设:
没有Repository。
页面:
直接:
调用:
接口。
现在:
需求变化:
原来:
服务器获取订单
改成:
本地缓存优先
那么:
所有页面:
全部修改。
例如:
有:
20个订单页面。
需要:
修改:
20次。
有Repository:
只改:
一个地方:
OrderRepository
即可。
八、Feature内部推荐目录
例如:
订单模块:
feature_order
│
├── pages
│ ├── OrderListPage.ets
│ ├── OrderDetailPage.ets
│
├── components
│ ├── OrderItem.ets
│
├── viewmodel
│ └── OrderViewModel.ets
│
├── repository
│ └── OrderRepository.ets
│
├── model
│ ├── Order.ets
│ └── Product.ets
│
├── service
│ └── OrderService.ets
│
├── constants
│
└── Index.ets
九、Model应该放哪里?
这是很多项目争议点。
例如:
订单对象:
export class Order {
id:string
amount:number
}
应该放:
哪里?
情况1:业务私有Model
例如:
只有订单使用:
Order
Refund
Delivery
放:
feature_order/model
即可。
情况2:多个业务共享
例如:
用户:
User
订单:
需要:
用户。
支付:
需要:
用户。
那么:
不要放:
feature_login。
应该:
提升:
core_account/model
或者:
common_model
十、HSP中的MVVM
公共能力:
也可以采用类似结构。
例如:
账号中心:
core_account(HSP)
│
├── model
│ User
│
├── repository
│ AccountRepository
│
├── service
│ LoginService
│
└── AccountManager
所有业务:
共享:
这个能力。
十一、网络层如何配合MVVM?
推荐:
不要:
业务直接:
调用:
HttpClient。
错误:
OrderPage
↓
HttpClient
正确:
OrderPage
↓
OrderVM
↓
OrderRepository
↓
ApiService
↓
HttpClient
例如:
Network模块:
core_network
提供:
HttpClient.request()
业务:
不知道:
底层实现。
十二、完整企业级架构图
最终:
一个大型HarmonyOS项目:
可以这样:
Entry
|
---------------------------------
Feature模块
| | |
Home Order Pay
| | |
---------------------------------
Repository
|
---------------------------------
Core HSP
Network Account Database Router
|
Common HAR
|
Base
十三、MVVM中的数据流
以订单刷新为例:
用户:
点击:
刷新按钮。
流程:
第一步
View:
Button()
.onClick(()=>{
vm.refresh()
})
第二步
ViewModel:
async refresh(){
this.orders =
await repository.getOrders()
}
第三步
Repository:
getOrders(){
return api.getOrders()
}
第四步
Network:
HttpClient.request()
第五步
服务器:
返回数据。
最终:
状态更新。
页面:
自动刷新。
十四、模块化 + MVVM 的优势
组合之后:
得到:
企业级架构:
业务隔离
订单不会污染登录
易测试
可以测试:
OrderRepository
OrderViewModel
不用启动页面。
易扩展
新增:
直播:
只增加:
feature_live
不用修改:
其它业务。
易维护
新人:
进入项目:
看到:
feature_xxx
core_xxx
common_xxx
马上理解。
十五、常见错误总结
错误1
View里面写网络:
http.get()
问题:
UI和数据耦合。
错误2
Feature直接调用Feature:
Order
↓
Login
问题:
循环依赖。
错误3
所有Model放Common:
结果:
common_model
越来越大。
应该:
业务私有归业务。
共享才提升。
错误4
Repository不存在。
结果:
业务代码:
直接操作:
网络、数据库。
后期:
无法维护。
本章总结
本章讲解了:
- HarmonyOS模块化中的MVVM设计;
- View、ViewModel、Repository职责;
- Feature内部目录规范;
- Model放置原则;
- HSP如何承载公共业务能力;
- 数据流完整过程。
最终形成:
Feature负责业务,View负责展示,ViewModel负责状态,Repository负责数据,Core负责能力。
这是目前大型HarmonyOS企业项目中最常见的架构模式。
第七章 企业级网络层设计(core_network HSP 实战)
在企业级 HarmonyOS 项目中,网络层(Network Layer)几乎是所有业务模块都会依赖的能力。
例如:
首页
│
订单
│
支付
│
登录
│
消息
│
个人中心
所有模块都会请求服务器。
因此,网络层绝不能写在某个 Feature 中。
例如下面这种设计:
feature_login
├── LoginPage
├── LoginApi
├── HttpClient
├── Interceptor
└── Request
看似没问题。
但是很快:
订单模块:
feature_order
也需要:
HttpClient
购物车:
也需要。
支付:
也需要。
最后:
整个项目:
出现:
HttpClient
HttpClient2
RequestUtil
NetManager
HttpHelper
甚至:
四五套网络框架。
这是企业开发最忌讳的问题。
一、为什么网络层必须独立?
因为:
它属于:
公共能力(Core Capability)
而不是:
业务。
例如:
网络:
可以服务:
登录
订单
支付
IM
直播
AI
商城
所以:
应该放:
core_network(HSP)
整个项目:
共享:
一份。
二、企业推荐网络模块结构
通常:
网络模块:
如下:
core_network
│
├── client
│ HttpClient.ets
│
├── api
│ UserApi.ets
│ OrderApi.ets
│ PayApi.ets
│
├── interceptor
│ AuthInterceptor.ets
│ LogInterceptor.ets
│ RetryInterceptor.ets
│
├── model
│ BaseResponse.ets
│
├── exception
│ ApiException.ets
│
├── config
│ NetworkConfig.ets
│
├── manager
│ TokenManager.ets
│
└── Index.ets
职责:
十分清晰。
三、网络层为什么采用 HSP?
很多同学会问:
为什么不用 HAR?
例如:
common_network.har
原因:
HAR:
编译:
复制。
例如:
feature_home
↓
common_network.har
订单:
feature_order
↓
common_network.har
支付:
feature_pay
↓
common_network.har
最终:
三个:
HttpClient。
三个:
Interceptor。
三个:
TokenManager。
浪费:
内存。
HSP:
则:
整个App:
只有:
HttpClient
一个实例。
例如:
FeatureHome
│
FeatureOrder
│
FeatureMine
│
──────────────
core_network(HSP)
──────────────
大家:
共享。
这也是 HarmonyOS 官方推荐 HSP 承载公共能力的重要原因。
四、网络请求完整流程
企业:
推荐:
如下:
Page
↓
ViewModel
↓
Repository
↓
Api
↓
HttpClient
↓
Interceptor
↓
Server
整个:
调用链:
固定。
例如:
订单:
页面:
vm.loadOrders()
↓
ViewModel:
repository.getOrders()
↓
Repository:
orderApi.queryOrders()
↓
Api:
httpClient.get()
↓
Http:
发送请求。
页面:
完全不知道:
接口细节。
五、HttpClient 封装
整个项目:
建议:
只有:
一个:
HttpClient。
例如:
export class HttpClient {
async get<T>(url: string): Promise<T> {
// 发起 GET 请求
throw new Error("TODO")
}
async post<T>(url: string, body: object): Promise<T> {
// 发起 POST 请求
throw new Error("TODO")
}
}
注意:
业务:
不要:
直接:
创建:
new HttpClient()
推荐:
单例:
export const httpClient = new HttpClient()
整个App:
共享。
六、为什么不要直接写 URL?
很多新人:
喜欢:
这样:
http.get(
"/user/login"
)
订单:
又写:
http.get(
"/order/list"
)
支付:
继续:
http.post(
"/pay"
)
最后:
URL:
散落:
整个项目。
维护:
困难。
正确:
抽:
Api。
例如:
UserApi
里面:
export class UserApi {
login(){
}
logout(){
}
}
订单:
OrderApi
里面:
export class OrderApi {
queryOrders(){
}
deleteOrder(){
}
}
所有:
接口:
集中。
七、Repository 不直接拼 URL
错误:
class OrderRepository{
getOrders(){
return http.get(
"/order/list"
)
}
}
Repository:
知道:
URL。
以后:
接口:
改了。
所有:
Repository:
修改。
正确:
Repository:
调用:
Api。
例如:
class OrderRepository{
getOrders(){
return orderApi.queryOrders()
}
}
URL:
全部:
放:
Api。
八、请求拦截器设计
企业:
都会:
有:
拦截器。
例如:
登录:
需要:
Token。
不要:
每次:
手写:
headers:{
token:token
}
正确:
统一:
拦截。
例如:
AuthInterceptor
负责:
自动:
添加:
Authorization
Token
Language
Version
DeviceId
所有:
请求:
统一。
九、日志拦截器
开发阶段:
需要:
打印:
接口。
例如:
Request URL
Method
Headers
Body
响应:
Status
Data
Time
Size
所以:
增加:
LogInterceptor
以后:
关闭:
日志:
只修改:
一个地方。
十、Token 自动刷新
企业:
登录:
都会:
过期。
例如:
服务器:
返回:
401
不要:
页面:
自己:
处理。
正确:
放:
AuthInterceptor
流程:
Request
↓
401
↓
RefreshToken
↓
重新请求
↓
返回成功
整个:
业务:
完全:
不知道。
十一、多环境配置
企业:
至少:
三个环境。
开发
测试
正式
不要:
写:
const BASE_URL =
"https://api.xxx.com"
推荐:
NetworkConfig
例如:
export class NetworkConfig{
static baseUrl:string=""
}
开发:
初始化:
https://dev.xxx.com
测试:
https://test.xxx.com
正式:
https://api.xxx.com
只修改:
配置。
十二、统一返回模型
不要:
接口:
返回:
各种:
类型。
推荐:
统一:
BaseResponse<T>
例如:
export class BaseResponse<T>{
code:number
message:string
data:T
}
订单:
返回:
BaseResponse<Order[]>
登录:
返回:
BaseResponse<User>
统一:
解析。
十三、异常统一处理
不要:
每个页面:
try{
}catch{}
推荐:
网络层:
统一:
抛:
ApiException
例如:
Token失效
网络错误
超时
服务器错误
解析错误
ViewModel:
只处理:
业务。
十四、完整网络调用流程
以订单列表为例:
OrderPage
│
▼
OrderViewModel
│
▼
OrderRepository
│
▼
OrderApi
│
▼
HttpClient
│
▼
AuthInterceptor
│
▼
LogInterceptor
│
▼
Server
│
▼
BaseResponse<Order[]>
│
▼
Repository
│
▼
ViewModel
│
▼
Page 自动刷新 UI
可以看到:
页面从始至终都不知道 URL、Token、Header、重试逻辑、异常处理。
这就是分层设计的价值。
十五、企业级 core_network 推荐目录
core_network
│
├── api
│ ├── UserApi.ets
│ ├── OrderApi.ets
│ ├── ProductApi.ets
│ ├── CartApi.ets
│ └── PayApi.ets
│
├── client
│ ├── HttpClient.ets
│ └── RequestBuilder.ets
│
├── interceptor
│ ├── AuthInterceptor.ets
│ ├── LogInterceptor.ets
│ ├── RetryInterceptor.ets
│ └── CacheInterceptor.ets
│
├── config
│ ├── NetworkConfig.ets
│ └── Environment.ets
│
├── manager
│ └── TokenManager.ets
│
├── model
│ ├── BaseResponse.ets
│ └── ApiResult.ets
│
├── exception
│ ├── ApiException.ets
│ └── ErrorCode.ets
│
├── utils
│ └── UrlUtil.ets
│
└── Index.ets
这样的结构可以支撑几十个业务模块共享同一套网络能力。
十六、本章总结
本章重点讲解了 HarmonyOS 模块化中的网络层设计:
- 为什么网络层应该放在 core_network(HSP);
- 为什么不建议放在 Feature 或 HAR;
- HttpClient、Api、Repository 的职责划分;
- 拦截器(认证、日志、重试)的设计;
- Token 自动刷新、多环境切换、统一返回模型;
- 企业级网络模块推荐目录。
核心原则可以总结为一句话:
网络层只负责通信,Repository 负责业务数据,ViewModel 负责页面状态,View 只负责展示。
这样不仅能保证代码职责清晰,也便于团队协作和后续扩展。
第八章 企业级 Repository 设计(Repository 模式深度解析)
前面我们已经完成了网络层(core_network)的设计。
很多开发者会产生一个疑问:
既然已经有 HttpClient 了,为什么还需要 Repository?
例如:
下面这种代码很多项目都有:
Button("加载订单")
.onClick(async () => {
const result = await httpClient.get("/order/list")
this.orders = result.data
})
代码看起来很简单。
为什么企业项目却很少这样写?
因为:
随着业务越来越复杂,这种写法很快就会失控。
一、什么是 Repository?
Repository(仓库)可以理解为:
数据统一管理中心(Data Repository)
它最大的职责就是:
向上提供统一的数据接口,向下屏蔽数据来源。
例如:
订单列表:
页面只需要:
repository.getOrderList()
至于:
数据来自:
网络
数据库
缓存
本地文件
云端同步
页面:
完全不知道。
Repository 在整个架构中的位置
企业项目通常如下:
UI(Page)
│
ViewModel
│
Repository
│
──────────────
Network
Database
Cache
File
──────────────
Repository:
就是:
所有数据来源的统一入口。
二、没有 Repository 会怎样?
例如:
订单页面:
@Component
export struct OrderPage {
async aboutToAppear(){
const data = await httpClient.get()
}
}
第二个页面:
OrderDetailPage
又写:
httpClient.get()
第三个页面:
退款:
继续:
httpClient.get()
整个项目:
几十个页面:
全部:
直接访问:
Http。
问题:
越来越多。
问题一:重复代码
例如:
每个页面:
都有:
try{
}catch{}
都有:
Loading.show()
都有:
Toast.show()
都有:
response.code==200
几十个页面:
重复。
问题二:缓存难以统一
现在:
需求:
新增:
先读取本地缓存,再请求网络。
如果:
页面:
直接:
调用:
Http。
那么:
所有页面:
全部修改。
例如:
50个页面。
修改:
50次。
问题三:数据库同步困难
例如:
订单:
服务器返回。
需要:
保存:
数据库。
没有Repository。
所有页面:
自己:
写:
db.insert()
项目:
越来越乱。
三、Repository 最大价值
Repository:
负责:
统一:
数据来源。
例如:
页面:
调用:
repository.getOrders()
Repository:
内部:
可以:
这样:
数据库
↓
没有
↓
请求网络
↓
保存数据库
↓
返回数据
页面:
不知道。
以后:
如果:
需求:
改成:
缓存优先
Repository:
改。
页面:
一行:
不用动。
四、企业 Repository 目录
例如:
订单:
feature_order
│
├── repository
│ OrderRepository.ets
│
├── datasource
│ RemoteDataSource.ets
│ LocalDataSource.ets
│
├── model
│
└── pages
是不是:
比:
全部:
放:
Repository:
更清晰?
五、Repository 与 DataSource
很多互联网公司:
Repository:
下面:
还会:
继续:
拆:
DataSource。
为什么?
例如:
订单:
来源:
有:
HTTP
Database
Cache
如果:
全部:
写:
OrderRepository。
以后:
Repository:
超过:
3000行。
所以:
继续:
拆。
RemoteDataSource
负责:
服务器。
例如:
class RemoteDataSource{
getOrders(){
}
}
里面:
全部:
网络。
LocalDataSource
负责:
数据库。
例如:
class LocalDataSource{
queryOrders(){
}
}
里面:
全部:
数据库。
Repository:
只负责:
协调。
例如:
Repository
↓
Remote
↓
Local
六、Repository 工作流程
企业:
推荐:
如下:
Page
↓
ViewModel
↓
Repository
↓
LocalDataSource
↓
没有数据
↓
RemoteDataSource
↓
保存数据库
↓
返回ViewModel
↓
刷新UI
整个:
流程:
固定。
七、Repository 示例
例如:
订单:
export class OrderRepository {
async getOrderList() {
// 1、先查询数据库
// 2、没有再请求服务器
// 3、保存数据库
// 4、返回结果
}
}
页面:
永远:
只有:
vm.loadOrders()
八、Repository 为什么不能放 Network?
很多新人:
喜欢:
这样:
core_network
↓
UserRepository
↓
OrderRepository
这是:
错误。
为什么?
Repository:
属于:
业务。
不是:
网络。
例如:
订单:
Repository:
应该:
放:
feature_order
登录:
Repository:
放:
feature_login
网络:
只负责:
请求。
九、多个 Repository 如何协同?
例如:
首页:
需要:
商品
用户
广告
优惠券
是不是:
一个:
Repository?
不是。
企业:
一般:
多个:
Repository。
例如:
HomeRepository
UserRepository
BannerRepository
CouponRepository
ViewModel:
组合:
调用。
不要:
一个:
万能Repository。
十、Repository 不要返回 HttpResponse
错误:
repository.getOrders()
↓
返回
HttpResponse
页面:
开始:
判断:
response.code
Repository:
失去:
意义。
正确:
Repository:
应该:
返回:
业务数据。
例如:
Order[]
或者:
User
而不是:
Http对象。
十一、Repository 不要关心 UI
错误:
Loading.show()
写:
Repository。
或者:
Toast.show()
写:
Repository。
Repository:
不知道:
页面。
否则:
以后:
单元测试:
全部失败。
十二、Repository 与 ViewModel 的职责
很多新人:
容易:
写反。
正确:
如下:
| ViewModel | Repository |
|---|---|
| 页面状态 | 数据获取 |
| Loading状态 | 网络请求 |
| Button点击 | 数据缓存 |
| 页面刷新 | 数据同步 |
| UI逻辑 | 网络逻辑 |
一句话:
ViewModel:
负责:
页面。
Repository:
负责:
数据。
十三、Repository 最佳实践
企业:
推荐:
一个:
Feature:
一个:
Repository。
例如:
feature_order
↓
OrderRepository
里面:
管理:
订单
退款
售后
物流
不要:
拆:
RefundRepository
ExpressRepository
EvaluateRepository
太细。
维护:
困难。
十四、Repository + HSP
如果:
多个:
Feature:
共享:
Repository。
例如:
账号。
可以:
放:
core_account(HSP)
↓
AccountRepository
订单:
登录:
支付:
共同:
依赖。
十五、完整企业架构
OrderPage
│
▼
OrderViewModel
│
▼
OrderRepository
┌────────┴────────┐
▼ ▼
LocalDataSource RemoteDataSource
▼ ▼
Database HttpClient
这就是目前企业项目最主流的 Repository 架构。
十六、Repository 常见错误
错误一:Repository 写 SQL
如果数据库操作复杂,可以进一步抽出 DAO(Data Access Object)层,让 Repository 只负责业务编排。
Repository
│
▼
DAO
│
▼
Database
错误二:Repository 调用 Router
例如:
router.pushUrl()
Repository:
不能:
跳页面。
错误三:Repository 操作组件
例如:
Dialog.show()
不能。
错误四:Repository 保存页面状态
例如:
isLoading=true
应该:
ViewModel。
十七、企业目录推荐
feature_order
│
├── pages
│
├── components
│
├── viewmodel
│
├── repository
│ └── OrderRepository.ets
│
├── datasource
│ ├── LocalDataSource.ets
│ ├── RemoteDataSource.ets
│ └── CacheDataSource.ets
│
├── dao
│ └── OrderDao.ets
│
├── model
│
├── service
│
└── Index.ets
对于大型项目,这种结构既能保持职责清晰,又方便后续扩展。
本章总结
本章详细讲解了 HarmonyOS 企业级 Repository 设计,重点包括:
- Repository 的定位与职责;
- Repository 与 ViewModel、Network 的边界;
- Repository + DataSource + DAO 分层设计;
- 为什么 Repository 不应该依赖 UI;
- 多数据源(网络、缓存、数据库)的统一管理;
- 企业项目推荐的目录结构。
核心原则:
Repository 负责"数据从哪里来",ViewModel 负责"数据怎么展示",View 负责"数据如何呈现"。
三者职责越清晰,项目越容易维护。
更多推荐



所有评论(0)