HarmonyOS掌上记账APP开发实践第2篇:从零搭建记账应用 — MoneyTrack 工程全景解析
概述
当开发者第一次接触一个新的鸿蒙应用工程时,面对纷繁的目录结构和配置文件,往往不知从何入手。理解工程的骨架和各个配置文件的用途,是高效开发和定位问题的第一步。MoneyTrack 基于 HarmonyOS Stage 模型构建,采用标准的多模块 HAR 工程布局,其工程结构代表了当前鸿蒙商业级应用的典型范式。本文从工程目录结构出发,逐一解析 AppScope 全局配置、products 入口模块、commons 通用库、components 业务组件和 features 特性页面的完整划分与设计思路,并深入分析关键配置文件的细节,帮助读者建立起鸿蒙应用工程的全局认知。
核心知识点
1. Stage 模型工程结构
HarmonyOS Stage 模型的工程包含三个主要区域:
- AppScope:存放全局应用配置,包括
app.json5(bundleName、版本号、图标等)和resources目录(全局资源),是应用级元数据的集中管理区域。 - 模块(modules):每个 HAR 或 entry 模块独立存放,包含
src/main/module.json5(模块配置)、src/main/ets/(ArkTS 代码目录)、src/main/resources/(模块级资源目录)。 - 编译配置:根目录
build-profile.json5定义所有模块和构建目标,hvigor/hvigor-config.json5配置构建工具链参数。
MoneyTrack 的完整工程目录结构如下:
2. AppScope 配置详解
AppScope/app.json5 定义应用的全局元数据:
{
"app": {
"bundleName": "com.hyh.moneyTrack",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
}
- bundleName:应用唯一标识,格式通常为反域名形式,在华为应用市场发布后不可更改。
- versionCode:整型版本号,用于应用市场的版本比较和灰度发布,此处
1000000对应1.0.0。 - versionName:面向用户的版本名称,显示在应用详情页。
- icon/label:引用
resources/base/element/string.json和resources/base/media/目录下的资源。
资源文件位于 AppScope/resources/base/ 目录,包含 element/string.json(字符串国际化)、media/(应用图标和分层图)。
3. products/entry 入口模块
products/entry 作为应用入口(type 为 “entry”),是唯一一个可独立安装的模块。其 module.json5 配置了完整的 Ability 体系:
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": ["phone", "tablet"],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"metadata": [
// todo: 修改client_id
{ "name": "client_id", "value": "******" }
],
"querySchemes": [
"weixin", "wxopensdk", "qqopenapi", "https"
],
"abilities": [
{
"name": "EntryAbility",
"supportWindowMode": ["fullscreen", "floating"],
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:app_icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
],
"requestPermissions": [
{ "name": "ohos.permission.PUBLISH_AGENT_REMINDER" }
],
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ets",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
关键配置说明:
- mainElement:指定应用启动时加载的 Ability(
EntryAbility),声明了skills中entity.system.home和action.system.home表示这是桌面入口。 - deviceTypes:支持 phone 和 tablet 两种设备,系统会根据运行设备自动适配形态。
- querySchemes:声明应用需要查询的 URL Scheme,包括
weixin(微信支付/登录)、qqopenapi(QQ 互联)、https(通用 Web 跳转),是实现第三方登录和支付的必要配置。 - requestPermissions:声明
PUBLISH_AGENT_REMINDER权限,用于账单提醒和通知。 - extensionAbilities:注册
EntryFormAbility(类型为form),用于提供服务卡片能力。
EntryAbility 的实际代码如下——它在 onCreate 中设置应用主题色,在 onWindowStageCreate 中加载主页面:
export default class EntryAbility extends UIAbility {
public onCreate(want: Want): void {
this.context.getApplicationContext()
.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
this.handleWantInfo(want);
}
public onNewWant(want: Want): void {
this.handleWantInfo(want);
}
public onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/MainEntry', (err) => {
if (err.code) {
Logger.error(TAG, 'Failed to load the content.' + JSON.stringify(err));
return;
}
contextUtil.setUiContext(windowStage.getMainWindowSync().getUIContext());
});
}
}
主入口页面 MainEntry.ets 使用 Tabs 组件组织四个 Tab 页面,每个 Tab 对应一个特性模块的视图:
@Entry
@ComponentV2
struct MainEntry {
vm: MainEntryVM = MainEntryVM.instance;
build() {
Navigation(this.vm.navStack) {
Tabs({ barPosition: BreakpointSystem.isTablet ? BarPosition.Start : BarPosition.End,
index: this.vm.curIndex, controller: this.vm.tabController }) {
TabContent() { HomeView() }
.tabBar(this.tabBarBuilder(TabIndexMap.HOME))
TabContent() { StatisticsView() }
.tabBar(this.tabBarBuilder(TabIndexMap.STATISTICS))
TabContent() { AssetsView() }
.tabBar(this.tabBarBuilder(TabIndexMap.ASSETS))
TabContent() { MineView() }
.tabBar(this.tabBarBuilder(TabIndexMap.MINE))
}
}
}
}
4. 构建配置:hvigor-config.json5
hvigor/hvigor-config.json5 控制鸿蒙构建工具链的行为:
{
"modelVersion": "5.0.2",
"dependencies": {},
"execution": {
// "analyze": "normal", /* 构建分析模式:normal | advanced | false */
// "daemon": true, /* 守护进程编译,加速重复构建 */
// "incremental": true, /* 增量编译,只重新编译变更文件 */
// "parallel": true, /* 并行编译,多模块同时构建 */
// "typeCheck": false, /* 是否启用类型检查 */
},
"logging": {
// "level": "info" /* 日志级别:debug | info | warn | error */
},
"debugging": {
// "stacktrace": false /* 编译错误时是否输出完整堆栈 */
},
"nodeOptions": {
// "maxOldSpaceSize": 8192 /* Node.js 最大堆内存,单位 MB */
}
}
这些配置项对构建性能有直接影响:
- 并行编译(parallel):21 个模块的工程开启并行编译后,构建时间可从数分钟缩短至数十秒。
- 增量编译(incremental):开发阶段只编译发生变化的文件,大幅提升热重载效率。
- 类型检查(typeCheck):Release 构建建议开启,Debug 构建可关闭以加快编译速度。
5. 各层目录结构详析
commons 通用层
commons/commonlib 是项目的"工具箱",其内部结构展示了通用模块的最佳组织方式:
commons/commonlib/src/main/ets/
├── components/ # 通用 UI 组件
│ ├── CommonButton.ets
│ ├── CommonDayPicker.ets
│ ├── CommonDivider.ets
│ ├── CommonHeader.ets
│ ├── CommonMonthPicker.ets
│ ├── ContainerColumn.ets
│ └── ContainerRow.ets
├── constants/ # 常量与枚举
│ ├── CommonConstants.ets
│ └── CommonEnums.ets
├── dialogs/ # 通用弹窗
│ └── CommonConfirmDialog.ets
└── utils/ # 工具类
├── eventbus/ # 事件总线
├── framework/ # 框架工具(Tab 切换等)
├── logger/ # 日志封装
├── router/ # 路由管理(RouterModule)
├── userinfo/ # 用户信息管理
├── window/ # 窗口工具
├── BreakpointSystem.ets # 断点适配系统
├── ContextUtil.ets # Context 工具
└── ReminderUtil.ets # 提醒工具
commons/lib_network 封装了网络层:
commons/lib_network/src/main/ets/
├── constants/Enums.ets # 网络相关枚举
├── https/
│ ├── apis/ # 业务 API
│ │ ├── Asset.ets
│ │ ├── Bill.ets
│ │ └── User.ets
│ └── Request.ets # Axios 请求封装
├── httpsmock/ # Mock 数据
│ ├── mockapis/ # Mock API 实现
│ └── MockAdapter.ets
└── types/responses/ # 响应类型定义
├── BaseResponse.ets
└── UserResponse.ets
components 组件层(共 14 个模块)
每个组件模块遵循一致的内部结构。以 bill_base 为例:
components/bill_base/src/main/ets/
├── commons/
│ ├── BillConstants.ets # 常量
│ ├── Enums.ets # 枚举(BalanceChangeType 等)
│ ├── FamilyModels.ets # 家庭账本数据模型
│ ├── Models.ets # 核心数据模型
│ └── Types.ets # TypeScript 类型定义
└── utils/
├── BreakpointSystem.ets # 断点适配
├── Logger.ets # 日志
└── ResourceUtil.ets # 资源工具
以 collect_personal_info 为例的复杂组件模块:
components/collect_personal_info/src/main/ets/
├── common/
│ ├── ConfigModel.ets
│ ├── Constant.ets
│ ├── CustomModifier.ets
│ ├── ErrorMsgMap.ets
│ ├── FormModel.ets
│ ├── GridRowColSetting.ets
│ └── Types.ets
├── components/ # 表单子组件
│ ├── ChooseAvatar.ets
│ ├── ChooseDate.ets
│ ├── EnterPhoneNumber.ets
│ ├── EnterTextArea.ets
│ ├── EnterTextInput.ets
│ └── SelectDialog.ets
├── viewmodels/
│ └── PersonalInfoViewVM.ets
└── views/
├── ListCard.ets
└── PersonalInfoView.ets
features 特性层(共 4 个模块)
以 home 为例——它是最核心的特性模块:
features/home/src/main/ets/
├── commons/
│ ├── Constants.ets # 首页常量
│ ├── Enums.ets # 首页枚举(弹窗类型等)
│ ├── Models.ets # 首页数据模型
│ └── Types.ets # 类型定义
├── components/ # 首页子组件
│ ├── AccountBookSelector.ets
│ ├── MonthlyBudgetCard.ets
│ └── TypeFilterSheet.ets
├── viewmodels/ # ViewModel 层
│ ├── AccountBookVM.ets
│ ├── BillDetailVM.ets
│ └── HomeVM.ets
└── views/ # 页面视图
├── AccountBookDetailPage.ets
├── AccountBookManagePage.ets
├── AccountBookPage.ets
├── BillDetailPage.ets
├── HomeView.ets
└── ResourceManagePage.ets
以 assets 为例:
features/assets/src/main/ets/
├── commons/Constants.ets
├── components/
│ ├── AssetFilterSheet.ets
│ ├── AssetLineChart.ets
│ └── BalanceTypeSelection.ets
├── viewmodels/
│ ├── AssetAnalysisVM.ets
│ ├── AssetDetailVM.ets
│ └── AssetsVM.ets
└── views/
├── AddAssetPage.ets
├── AssetAnalysisPage.ets
├── AssetDetailPage.ets
└── AssetsView.ets
6. 关键配置文件速查表
| 配置文件 | 路径 | 核心作用 | 关键字段 |
|---|---|---|---|
app.json5 |
AppScope/app.json5 |
应用级元数据 | bundleName、versionCode、versionName |
module.json5 |
products/entry/src/main/module.json5 |
入口模块配置 | abilities、extensionAbilities、deviceTypes、querySchemes |
build-profile.json5 |
./build-profile.json5 |
模块注册与构建目标 | modules[].srcPath、products[].targetSdkVersion |
hvigor-config.json5 |
hvigor/hvigor-config.json5 |
构建工具链参数 | execution.parallel、execution.incremental |
oh-package.json5 |
每个模块根目录 | 模块元数据与依赖声明 | name、main、dependencies |
main_pages.json |
products/entry/src/main/resources/base/profile/main_pages.json |
页面路由注册 | src 数组列出所有页面路径 |
router_map.json |
各模块 resources/base/profile/ |
模块内路由映射 | NavDestination 名字与页面的映射关系 |
form_config.json |
products/entry/src/main/resources/base/profile/ |
服务卡片配置 | 卡片类型、更新周期、支持尺寸 |
最佳实践与注意事项
1. 统一包名与版本管理
bundleName 一旦发布即不可更改,因此要确保签名证书的 bundleName 与 app.json5 中一致。建议在项目启动阶段就确定最终的包名,并在 CI/CD 流水线中统一管理 versionCode 的自动递增逻辑。
2. Ability 配置的最小化原则
module.json5 只应包含应用模块级别的配置。Ability 内部的环境初始化逻辑应放在对应的 Ability 实现类中,而非集中写在配置文件中。例如,EntryAbility 中通过 handleWantInfo 处理外部拉起参数,通过 onWindowStageCreate 加载首页,逻辑清晰且便于测试。
3. querySchemes 的按需声明
querySchemes 数组声明的 Scheme 会影响应用市场审核。只需声明确实需要的 Scheme(如微信登录的 weixin),不需要的 Scheme 不应添加。同时要注意,querySchemes 仅允许查询,实际跳转还需要配合 openLink 等 API。
4. 资源管理的层级策略
全局共享的资源(如应用名称、图标)放在 AppScope/resources 中;模块内部独享的资源(如账单分类图标、字符串)放在各自模块的 resources 目录中。这样可以避免资源名冲突,也便于模块的独立发布和消费。
5. 构建配置的针对性调优
Debug 构建建议关闭类型检查、开启并行和增量编译以提升开发效率;Release 构建应开启所有检查项并关闭不必要的调试日志。在 hvigor-config.json5 中可根据构建模式做差异化配置。
总结
MoneyTrack 的工程全景展现了鸿蒙 Stage 模型下多模块应用的标准布局。从 AppScope 的全局配置到每个模块的自包含结构,从 module.json5 的 Ability 注册到 hvigor-config.json5 的构建优化,每个配置文件都有其明确的职责。理解这一工程结构,不仅能帮助开发者快速上手 MoneyTrack 项目,更能为构建自己的鸿蒙应用提供可复用的蓝图。核心思路是:将关注点分离原则贯彻到工程的每个角落——资源按层级归属、配置按模块自治、依赖按方向控制,这样才能构建出真正可维护、可扩展的鸿蒙应用。
参考文档
- HarmonyOS 应用工程结构指南
- Stage 模型开发说明
- module.json5 配置文件参考
- 项目源文件:
d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\build-profile.json5 - 入口配置:
d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\products\entry\src\main\module.json5 - 构建配置:
d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\hvigor\hvigor-config.json5 - 入口页面:
d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\products\entry\src\main\ets\pages\MainEntry.ets - Ability 实现:
d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\products\entry\src\main\ets\entryability\EntryAbility.ets
更多推荐



所有评论(0)