模块化、可复用、开源的 HarmonyOS 快速开发框架
为什么需要 HCompass?
鸿蒙生态正在高速发展,越来越多的开发者投入到 HarmonyOS 应用开发中。但在实际项目中,我们常常面临这些痛点:
- 每个新项目都要从零搭建基础架构,重复造轮子
- 网络请求、路由导航、状态管理等通用能力缺乏统一封装
- 多人协作时代码风格不一致,模块边界模糊
- 业务功能难以跨项目复用,积累的经验无法沉淀
HCompass 正是为解决这些问题而生。它不是一个 UI 组件库,而是一套完整的应用开发框架 -- 帮你把地基打好,让你专注于业务本身。
核心理念:功能包即积木
HCompass 的核心思想很简单:把应用拆成一个个独立的"功能包",像搭积木一样组合它们。
每个功能包都是一个自包含的业务模块,拥有自己的页面、ViewModel、服务和数据模型。需要登录功能?放入 auth 功能包。需要用户中心?放入 user 功能包。功能包之间通过契约层解耦,互不依赖,随时可以拆卸和替换。
你的应用 = Entry(入口) + 若干功能包(积木)
四层架构,职责分明
HCompass 采用清晰的四层架构设计:
| 层级 | 职责 | 关键特征 |
|---|---|---|
| Entry | 应用入口 | 初始化框架、注册功能包、配置路由 |
| Packages | 业务功能包 | 独立开发、独立测试、可跨项目复用 |
| Shared | 共享契约 | 定义服务接口和类型,功能包之间的"协议" |
| Core | 框架核心 | 与业务无关的通用能力,可直接迁移到任何项目 |
这种分层设计带来的好处是:Core 层可以直接复用到你的下一个项目,Packages 层的功能包可以按需组合,Shared 层确保模块之间的通信有据可依。
开箱即用的核心能力
依赖注入(DI)
轻量级 DI 容器,让功能包之间彻底解耦:
// 在 Shared 层定义契约
interface IUserRepository {
getUserInfo(id: string): Promise<User>;
}
// 在功能包中实现并注册
register<IUserRepository>(ServiceKeys.USER_REPO, new UserRepositoryImpl());
// 在任何地方解析使用
const userRepo = resolve<IUserRepository>(ServiceKeys.USER_REPO);
不再需要硬编码依赖关系,功能包只依赖契约,不依赖具体实现。
导航系统
统一的路由管理,支持路由守卫:
// 路由跳转
NavigationService.push(UserRoutes.PROFILE, { userId: "123" });
// 路由守卫 -- 未登录自动跳转登录页
GuardManager.addGuard(new LoginGuard());
支持登录守卫、权限守卫、条件守卫,灵活组合,按优先级执行。
网络请求
基于 Axios 封装,告别重复的请求模板代码:
// 继承 BaseNetWorkViewModel,自动管理 loading/error/success 状态
@ObservedV2
class UserProfileViewModel extends BaseNetWorkViewModel<User> {
async fetchData(): Promise<void> {
await this.request(() => this.userRepo.getUserInfo(this.userId));
}
}
内置拦截器链、统一错误处理、分页逻辑,你只需关注业务数据。
设计系统
统一的设计令牌,确保 UI 一致性:
// 百分比常量,告别魔法数字
Row() { ... }.width(P100).height(P50)
// 间距组件,统一视觉节奏
Column() {
Text("标题")
SpaceVerticalMedium() // 12vp 间距
Text("内容")
}
基础父类
三个 ViewModel 基类覆盖 90% 的页面场景:
| 基类 | 适用场景 |
|---|---|
BaseViewModel | 通用页面,提供生命周期管理 |
BaseNetWorkViewModel | 网络请求页面,自动处理 loading/error/success |
BaseNetWorkListViewModel | 分页列表页面,内置下拉刷新和上拉加载 |
实战案例:聚合登录功能包
光说架构不够直观,我们直接看官方实现的 login 功能包 -- 一个支持华为账号一键登录、微信、支付宝、短信验证码的聚合登录模块,完整展示了功能包从契约定义到页面渲染的全流程。
功能包目录结构
packages/login/
├── LoginModule.ets # 模块生命周期(DI注册 + 路由 + 守卫)
├── view/
│ ├── LoginPage.ets # 主登录页(华为一键登录 + 三方登录)
│ └── SmsLoginPage.ets # 短信验证码登录页
├── viewmodel/
│ ├── LoginViewModel.ets # 登录业务逻辑
│ └── SmsLoginViewModel.ets # 短信登录逻辑
├── components/
│ ├── AnimatedAuthPage.ets # 认证页面基础布局
│ ├── PhoneInputField.ets # 手机号输入组件
│ ├── VerificationCodeField.ets # 验证码输入组件
│ ├── UserAgreement.ets # 用户协议组件
│ └── ...
├── navigation/
│ ├── LoginNav.ets # 登录页导航构建器
│ └── SmsLoginNav.ets # 短信登录页导航构建器
├── services/
│ └── AuthNavSvcImpl.ets # 导航服务实现
└── models/
└── Constant.ets # 三方 APP_ID 配置
View / ViewModel / Service / Navigation 各司其职,结构一目了然。
第一步:在 Shared 层定义契约
功能包对外暴露的能力,全部通过 Shared 层的接口契约来声明:
// shared/contracts -- 导航服务契约
export const AUTH_NAV_SVC_KEY: string = "authNavService";
export interface IAuthNavSvc {
toLogin(): void; // 跳转登录页
toSmsLogin(): void; // 跳转短信登录页
}
// shared/contracts -- 路由常量
export class AuthRoutes {
static Login = "auth/login";
static SmsLogin = "auth/sms-login";
}
其他功能包只需要依赖这些接口,不需要知道登录页长什么样、用了哪个 SDK。
第二步:注册模块 -- 一个类搞定 DI、路由、守卫
LoginModule 实现 FeatureModule 接口,框架会在启动时自动调用:
export class LoginModule implements FeatureModule {
readonly moduleId: string = 'auth';
readonly moduleName: string = '认证模块';
readonly version: string = '1.0.0';
readonly dependencies: string[] = [];
// 注册服务到 DI 容器
registerServices(container: Container): void {
container.register<IAuthNavSvc>(AUTH_NAV_SVC_KEY, () => new AuthNavSvcImpl());
}
// 注册页面路由
registerRoutes(registry: RouteRegistry): void {
registry.register(AuthRoutes.Login, loginNavBuilderWrapper);
registry.register(AuthRoutes.SmsLogin, smsLoginNavBuilderWrapper);
}
// 注册路由守卫 -- 未登录自动拦截
registerGuards(navigationService: NavigationService): void {
navigationService.registerGuard(new AuthGuard());
}
}
三个方法,把服务注入、路由注册、登录守卫全部声明完毕。当用户访问受保护的页面时,AuthGuard 会自动检查登录状态,未登录则跳转到登录页:
class AuthGuard implements RouteGuard {
readonly name: string = 'AuthGuard';
readonly priority: number = 100; // 优先级最高
canActivate(context: RouteContext): boolean {
return getUserState().isLoggedIn(); // 已登录放行,未登录拦截
}
onReject(context: RouteContext): void {
// 拦截后自动跳转登录页
navigation?.navigateTo(AuthRoutes.Login, context.params);
}
}
第三步:ViewModel 封装业务逻辑
LoginViewModel 继承 BaseViewModel,集中处理多种登录方式:
@ObservedV2
export default class LoginViewModel extends BaseViewModel {
@Trace anonymousPhone: string = ""; // 华为账号匿名手机号
// 华为账号一键登录控制器
huaweiLoginController: LoginWithHuaweiIDButtonController =
new LoginWithHuaweiIDButtonController()
.setAgreementStatus(AgreementStatus.ACCEPTED)
.onClickLoginWithHuaweiIDButton((error, response) => {
this.handleLoginWithHuaweiIDButton(error, response);
});
// 微信 OAuth 登录
async onWechatLoginClick(): Promise<void> {
const share: ShareWxSdk = new ShareWxSdk(APP_ID_WX);
const res = await share.wechatOAuth("snsapi_userinfo", state, () => {});
if (res instanceof SendAuthResp) {
const userInfo = await share.getUserInfo(APP_SECRET_WX, res);
// 处理登录结果...
}
}
// 支付宝授权登录
onAlipayLoginClick(): void {
AFServiceCenter.call(AFService.AFServiceAuth, params);
}
}
View 层不包含任何业务逻辑,只负责绑定 ViewModel 的状态和方法。
第四步:View 层 -- 声明式 UI 渲染
@ComponentV2
export struct LoginPage {
@Local private vm: LoginViewModel = new LoginViewModel();
build(): void {
AppNavDestination({ viewModel: this.vm }) {
// Logo
LogoIcon()
// 匿名手机号展示
Text(this.vm.anonymousPhone)
// 华为账号一键登录按钮
LoginWithHuaweiIDButton({
params: { style: Style.BUTTON_CUSTOM, loginType: LoginType.QUICK_LOGIN },
controller: this.vm.huaweiLoginController,
})
// 短信验证码登录
IBestButton({
text: $r("app.string.sms_login"),
onBtnClick: () => {
getContainer().tryResolve<IAuthNavSvc>(AUTH_NAV_SVC_KEY)?.toSmsLogin();
}
})
// 第三方登录(微信 / 支付宝 / QQ)
this.buildThirdPartyLogin()
// 用户协议
UserAgreement()
}
}
}
注意跳转短信登录页时,通过 DI 容器解析 IAuthNavSvc 服务来导航,而不是直接引用目标页面 -- 这就是契约解耦的威力。
第五步:可复用组件沉淀
login 功能包还沉淀了一组可复用的认证 UI 组件:
// AnimatedAuthPage -- 认证页面通用布局,接收自定义内容
@ComponentV2
export struct AnimatedAuthPage {
@Param title: ResourceStr = "";
@BuilderParam content: CustomBuilder;
build(): void {
LargePaddingVerticalScroll({ fillMaxSize: true }) {
Text(this.title).fontSize(28).fontWeight(FontWeight.Medium)
SpaceVerticalXXLarge()
if (this.content) { this.content(); }
}
}
}
// 短信登录页直接复用这个布局
AnimatedAuthPage({ title: $r("app.string.welcome_login") }) {
PhoneInputField({ ... })
VerificationCodeField({ ... })
UserAgreement()
IBestButton({ text: $r("app.string.login"), ... })
}
PhoneInputField、VerificationCodeField、UserAgreement 这些组件,在你开发注册页、找回密码页时可以直接复用。
小结:一个功能包的完整生命周期
Shared 层定义契约(接口 + 路由 + 类型)
|
LoginModule 注册服务、路由、守卫
|
ViewModel 封装业务逻辑(华为/微信/支付宝/短信)
|
View 层声明式渲染(绑定 ViewModel,零业务代码)
|
可复用组件沉淀(AnimatedAuthPage / PhoneInputField / ...)
这就是 HCompass 功能包的开发范式。每个功能包都遵循同样的模式,新成员看一个包就能上手所有包。
技术栈
| 技术 | 用途 |
|---|---|
| HarmonyOS NEXT | 开发平台 |
| ArkTS / ArkUI | 开发语言与 UI 框架 |
| @ohos/axios | HTTP 请求 |
| @ibestservices/ibest-ui-v2 | UI 组件库 |
| @ibestservices/ibest-orm | 数据库 ORM |
谁适合使用 HCompass?
- 独立开发者:快速搭建应用骨架,把精力放在业务创新上
- 创业团队:统一技术栈和代码规范,降低协作成本
- 企业开发团队:沉淀业务功能包,跨项目复用,提升交付效率
- 鸿蒙生态贡献者:以功能包为单位贡献开源模块,共建生态
完善的文档体系
HCompass 提供了基于 VitePress 构建的完整文档站点,涵盖:
- 快速开始 -- 从零搭建你的第一个 HCompass 应用
- 架构设计 -- 深入理解四层架构和 MVVM 模式
- 核心模块 -- DI、导航、网络、组件等每个模块的详细 API 文档
- 功能包开发 -- 手把手教你创建和发布功能包
- 最佳实践 -- 代码规范、性能优化、安全指南、状态管理
快速开始
# 克隆项目
git clone https://github.com/codelably/HCompass.git
# 使用 DevEco Studio 打开项目
# 等待依赖同步完成
# 运行 entry 模块即可体验
环境要求:
- DevEco Studio 5.0+
- HarmonyOS SDK 最新稳定版
- Node.js 18.0+
开源协议
HCompass 基于 MIT 协议开源,你可以自由使用、修改和分发。
欢迎通过 Issue 和 Pull Request 参与项目建设,一起让鸿蒙开发更简单。
- GitHub:github.com/codelably/H…
- AtomGit:atomgit.com/codelably/H…
- 文档:hcompass.codelably.com