项目分包方案:模块拆分、跨模块组件与方法引用规范
项目分包方案:模块拆分、跨模块组件与方法引用规范
项目规模小的时候,所有页面、工具类、组件都写在一个Entry Module里,开发效率很高。但随着业务迭代,页面越堆越多,代码量涨到几十万行,问题就来了:改一个功能要全项目编译,启动速度变慢,多人协作时代码冲突频繁,公共组件散落在各个角落,想复用还得复制粘贴。这时候就需要做分包——把项目拆成多个模块,按业务和职责分层。本文基于最新鸿蒙Stage模型,讲清楚模块怎么拆、跨模块组件怎么复用、跨模块方法怎么调用,附带可直接落地的代码示例。
01 为什么要分包:单Module的问题
先说说单Module开发到底有哪些坑,为什么要花时间拆模块。
**编译速度慢。**项目越大,改一行代码全量编译的时间越长。本来改个文案要等两分钟,拆成模块后只编译当前模块,秒级出结果。
**代码耦合严重。**所有页面互相import,你引用我的工具类,我调用你的方法,最后牵一发而动全身,没人敢动老代码。
**公共组件复用难。**多个页面要用同一个按钮组件,要么复制粘贴到各个页面,要么硬塞到公共目录里,改一处要同步改好几份。
**权限管控不清楚。**所有页面都在一个模块里,谁都能改谁的代码,没有边界,新同学接手项目根本分不清哪些是公共的、哪些是业务的。
分包不是为了显得架构高级,而是为了解决这些实际问题。项目不大的时候不用硬拆,等代码量上来了、团队人多了,再拆也不迟。
02 模块拆分方案:按业务分层
鸿蒙项目拆分模块,一般按三层来分:公共基础层、业务功能层、主入口层。
**第一层:公共基础层(Common)**放所有业务模块都要用到的东西:网络请求封装、数据库工具、通用UI组件、常量定义、基础工具类。这一层只提供能力,不依赖任何业务模块。
**第二层:业务功能层(Feature)**按业务线拆成独立模块,比如用户中心模块、订单模块、支付模块、消息模块。每个业务模块是独立的HAR,只依赖公共基础层,不依赖其他业务模块。
**第三层:主入口层(Entry)**应用的入口Module,负责把各个业务模块组装起来,处理应用启动、全局配置、页面路由跳转。它依赖所有业务模块,是唯一可以看到所有模块的地方。
最终的项目结构大概是这样:
MyProject/
├── entry/ # 主入口Module
├── common/ # 公共基础层(HAR)
│ ├── src/main/ets/components/ # 通用组件
│ ├── src/main/ets/utils/ # 工具类
│ └── src/main/ets/net/ # 网络封装
├── feature_user/ # 用户中心模块(HAR)
├── feature_order/ # 订单模块(HAR)
├── feature_pay/ # 支付模块(HAR)
└── build-profile.json5 # 全局配置
拆分的原则很简单:依赖方向只能从上往下走,Entry依赖业务模块,业务模块依赖公共模块。不能反过来,公共模块不能依赖业务模块,不然就循环依赖了。
03 跨模块组件引用:HAR静态共享包
公共组件放在common模块里,其他业务模块要用,怎么引用?用HAR(Static Shared Library)静态共享包。
先在common模块里导出组件:
// common/src/main/ets/components/CommonButton.ets
@Component
export struct CommonButton {
@Prop text: string = '';
onClick: () => void = () => {};
build() {
Button(this.text)
.width('100%')
.height(44)
.backgroundColor('#0066FF')
.fontColor('#FFFFFF')
.borderRadius(8)
.onClick(() => this.onClick())
}
}
然后在common的Index.ets里统一导出:
// common/Index.ets
export { CommonButton } from './src/main/ets/components/CommonButton';
export { NetworkUtil } from './src/main/ets/utils/NetworkUtil';
export { Constants } from './src/main/ets/constants/Constants';
业务模块要用的时候,先在oh-package.json5里依赖common:
// feature_order/oh-package.json5
{
"dependencies": {
"common": "file:../common"
}
}
然后就可以直接引用了:
// feature_order/src/main/ets/OrderList.ets
import { CommonButton } from 'common';
@Entry
@Component
struct OrderList {
build() {
Column() {
CommonButton({ text: '提交订单', onClick: () => this.submitOrder() })
}
}
submitOrder() {
console.info('提交订单');
}
}
HAR是静态打包的,最终会编译进主Module里。好处是简单直接,组件和工具类直接import就能用;缺点是如果多个模块都依赖同一个HAR,最终包里会有一份副本,包体积会稍微大一点。
04 跨模块方法调用:路由与接口注册
业务模块之间不能直接互相import,不然就耦合死了。比如订单模块要跳转到用户中心的个人主页,不能直接import feature_user里的页面。这时候就要用路由来解耦。
鸿蒙官方提供了Navigation路由,跨模块跳转只需要知道页面路径,不需要知道具体实现。
// common/src/main/ets/router/RouterConstants.ets
export class RouterConstants {
static readonly USER_PROFILE = 'feature_user/ProfilePage';
static readonly ORDER_DETAIL = 'feature_order/DetailPage';
static readonly PAY_RESULT = 'feature_pay/ResultPage';
}
各个业务模块在自己的入口里注册页面路由:
// feature_user/src/main/ets/Index.ets
import { RouterConstants } from 'common';
// 页面注册到Navigation
export function registerUserPages(router: Navigation) {
router.registerPage(RouterConstants.USER_PROFILE, (ctx) => {
return UserProfilePage();
});
}
订单模块要跳用户主页,只需要用路由常量,不用依赖用户模块:
// feature_order/src/main/ets/OrderList.ets
import { RouterConstants } from 'common';
@Entry
@Component
struct OrderList {
@State pagePath: string = '';
build() {
Navigation(this.pagePath) {
Button('查看用户信息')
.onClick(() => {
// 直接跳用户主页,不需要import用户模块
this.pagePath = RouterConstants.USER_PROFILE;
})
}
}
}
这样做的好处是:订单模块根本不知道用户模块的存在,只知道一个路径字符串。以后用户模块改了页面实现,或者拆成单独的HSP,订单模块完全不用改代码。
除了页面跳转,跨模块调用方法也是同理。公共层定义接口,各模块自己实现,主入口在启动时注册实现,其他模块通过接口调用,不直接依赖具体实现。
05 依赖管理与引用规范
分包之后,依赖关系一定要管清楚,不然很快又会回到全耦合的状态。
**第一,依赖方向只能从上往下。**Entry → 业务模块 → 公共模块。公共模块不能依赖业务模块,业务模块之间不能互相依赖。一旦发现两个业务模块要互相调用,说明逻辑抽得不对,应该把公共部分提到公共层去。
**第二,导出要收敛。**每个模块只导出对外暴露的东西,内部实现不要导出。common模块的Index.ets就是出口,只有写在这里的,其他模块才能用。没写在这里的,就算import路径能找到,也不应该用。
**第三,公共组件要设计通用。**放到common里的组件,不能带业务逻辑。CommonButton就是个纯按钮,不能写"提交订单"这种业务文案。业务特有的组件,就放在对应的业务模块里,不要硬塞到公共层。
**第四,常量和枚举统一管理。**接口地址、页面路径、状态码这些,都放到common的常量目录里。不要在业务模块里自己定义一套,不然同一个状态码两个地方写了不一样的值,排查问题都费劲。
06 落地注意事项
分包改造不是一蹴而就的,要逐步推进,别想着一天就拆完。
**先抽公共层。**第一步先把所有页面都在用的工具类、通用组件、常量抽到common模块里。这一步风险最小,改完编译能过,页面功能不变。
**再拆业务模块。**公共层抽完之后,按业务线一个个拆。先拆最独立的模块,比如设置页、关于页,这些和其他业务耦合最少,拆起来最容易。
**最后处理跨模块跳转。**所有模块拆完之后,把原来直接import的跳转改成路由跳转。这一步工作量最大,但是解耦效果最明显。
还有一个常见的坑:HAR静态包最终都会打包进主Entry,如果公共模块太大,包体积会涨。这时候可以考虑用HSP(动态共享包),按需加载,不打包进主包里。但HSP配置更复杂,小项目用HAR就够了,不用上来就追求动态化。
小结
项目分包的核心不是分多少个模块,而是理清依赖关系。公共层放通用能力,业务层按线拆分,入口层负责组装。跨模块用路由和接口通信,不直接互相引用。项目不大的时候不用硬拆,等代码量上来、团队人多了,再按这个思路逐步拆分。先抽公共,再拆业务,最后做路由解耦,每一步都能独立上线,不会一下把项目搞崩。
更多推荐




所有评论(0)