HarmonyOS 7 module.json5 到底管什么?从 entry 模块的入口链路逐项拆解【鸿蒙心迹】

你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀
全文目录:
前言
刚创建一个 Stage 模型工程时,entry/src/main/module.json5 往往已经被 DevEco Studio 填好了。很多时候我们只是继续往里面加权限、Ability 配置,却没有认真区分:name 是谁的名字,mainElement 指向谁,pages 为什么又跳到一个 profile 文件,两个位置都出现的 srcEntry 到底是不是一回事。
这篇文章不扩散到 app.json5、build-profile.json5 或 oh-package.json5,只沿着一个最小 entry 模块,把 name、type、srcEntry、mainElement、deviceTypes、pages/profile、abilities 串起来看清楚。
本文以 Stage 模型为前提。华为官方 HarmonyOS 7.0 升级指导明确给出了 HarmonyOS 7.0 与 API 26.0.0 的对应关系,并建议面向该版本升级开发套件。需要特别说明的是,module.json5 中这些字段属于模块配置描述,并不是 ArkTS 可调用 API,因此不能简单给每个 JSON 字段套一个“API 首次支持版本”;涉及本文示例代码中的 Ability Kit 能力,则需要按 API 文档核对版本。
一、先别把 module.json5 当成“应用总配置”
一个典型的 entry 模块可以看到这样的结构:
{
"module": {
"name": "entry",
"type": "entry",
"srcEntry": "./ets/myabilitystage/MyAbilityStage.ets",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
],
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
]
}
}
先不管其他配置,只看这几个字段,可以把它理解成一条入口链:
entry 模块
│
├── name:这个 Module 叫什么
├── type:这个 Module 是什么类型
├── srcEntry:Module 级代码入口(配置 AbilityStage 时)
├── mainElement:这个 Module 的入口组件是谁
├── deviceTypes:这个 Module 面向哪些设备类型
├── pages:页面清单资源在哪里
└── abilities:这个 Module 注册了哪些 UIAbility
│
└── srcEntry:某个 UIAbility 的实现代码在哪里
这里最容易理解错的是三个“入口”:module.srcEntry、mainElement、abilities[].srcEntry。它们不是三个不同写法表达同一件事,而是处在不同层级。
华为官方当前示例中,entry 模块仍采用 name: "entry"、type: "entry"、mainElement: "EntryAbility"、pages: "$profile:main_pages" 和 abilities[].srcEntry 这样的组织方式。
二、name:给 Module 一个逻辑名字
"name": "entry"
这里描述的是当前 Module 的名称,不是应用的 Bundle 名,也不是页面名,更不是 UIAbility 名。
这个名称并不只存在于源码配置阶段。华为官方拆包工具对 HAP 信息的说明中,HapInfo.name 表示当前 module 的名字;官方 Native 跨模块调用 FAQ 也明确使用 module.json5 中 module.name 识别入口模块。
所以看到:
"module": {
"name": "entry"
}
可以先建立一个最简单的认知:
name回答的是“这个模块叫什么”。
不要把它和下面的:
"abilities": [
{
"name": "EntryAbility"
}
]
混在一起。前者是 Module 名,后者是 Ability 名,层级完全不同。
三、type:决定它是不是入口模块
"type": "entry"
本文只讨论 entry,因此不展开其他模块类型。
type: "entry" 表示当前 Module 是应用的入口类型模块。华为官方工程管理文档明确通过修改 module.json5 的 type 和 deviceTypes 配置模块,并说明同一个设备类型在一个工程中只支持一个 Entry 类型模块。官方构建 FAQ 也会根据 module.json5 的 type 判断模块对应的构建任务。
所以:
"name": "entry",
"type": "entry"
表达的是两件事:
name 是身份名称,type 是模块角色。
这里比较容易出现一个理解偏差:模块目录叫 entry,并不等于“因为目录名叫 entry,所以它天然就是入口模块”。从配置语义上真正声明模块类型的是 type。
四、srcEntry:先看清它出现在哪一层
srcEntry 是这几个字段里最容易看错的一个,因为它既可以出现在 module 层,也会出现在 abilities 的成员里。
1. module.srcEntry:指向 AbilityStage
例如:
{
"module": {
"name": "entry",
"type": "entry",
"srcEntry": "./ets/myabilitystage/MyAbilityStage.ets"
}
}
华为官方 AbilityStage 开发指导说明,AbilityStage 是 Module 级的组件容器,一个 AbilityStage 实例对应一个 Module。当应用的 HAP 首次加载时会创建 AbilityStage;如果需要使用自定义 AbilityStage,可以在 module.json5 的 Module 层通过 srcEntry 指定其代码路径。
对应的最小 ArkTS 可以写成:
import { AbilityStage } from '@kit.AbilityKit';
export default class MyAbilityStage extends AbilityStage {
onCreate(): void {
console.info('MyAbilityStage onCreate');
}
}
这段代码解决的不是“打开首页”,而是给 Module 增加一个 AbilityStage 级入口。真正需要关注的是:这个 srcEntry 位于 module 对象直属层级。
如果项目根本没有自定义 AbilityStage,就不要为了“配置看起来完整”硬加一个。官方文档也明确指出,DevEco Studio 默认工程不会自动生成 AbilityStage,需要使用时再创建。
2. abilities[].srcEntry:指向具体 UIAbility
再看另一个:
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
]
这里的 srcEntry 属于某一个 Ability 配置,它指向这个 Ability 对应的代码实现。华为官方多个当前示例都使用这种方式,把 EntryAbility 与具体的 .ets 文件关联起来。
所以不要只记“srcEntry 是入口文件”。更准确的理解应该是:
先看
srcEntry所在的配置层级,再判断它是谁的代码入口。
五、mainElement:它不是首页路径
"mainElement": "EntryAbility"
这个字段非常容易被理解成“首页”,但它并不直接指向 Index.ets。
华为官方拆包工具文档将 Stage 模型中的 mainElement 描述为 HAP 的入口 Ability 名称或者 Extension 名称。常见 entry 工程中,它配置为:
"mainElement": "EntryAbility"
而 abilities 中同时注册:
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
也就是说,在这个最小场景里,mainElement 找的是入口组件 EntryAbility,而 EntryAbility 的实现文件再由该 Ability 自己的 srcEntry 找到。
这条关系可以写成:
mainElement
↓
"EntryAbility"
↓
abilities[].name
↓
abilities[].srcEntry
↓
EntryAbility.ets
真正的 ArkUI 页面还在下一层。
这也是为什么把:
"mainElement": "pages/Index"
理解成“设置首页”的思路是不对的。mainElement 与页面路径不是同一个概念。
六、deviceTypes:声明这个 Module 面向哪些设备类型
例如:
"deviceTypes": [
"phone",
"tablet"
]
华为官方工程管理文档直接通过修改 deviceTypes 来配置 Module 支持的设备类型,并给出了 entry 模块配置 phone、tablet 的示例;当前官方 FAQ 中也可以看到加入 "2in1" 的配置方式。
因此这里回答的问题是:
当前这个 Module 面向哪些设备类型进行安装和运行。
它不是 ArkUI 响应式布局代码,也不会替你完成不同屏幕尺寸下的界面适配。
例如:
"deviceTypes": [
"phone",
"tablet",
"2in1"
]
表达的是模块支持范围发生了变化,不等于现有页面已经自动完成 Phone、Tablet、2in1 的布局适配。
官方拆包工具最终解析出的 PackInfo.deviceType 同样表示当前 HAP 包所支持的设备类型,这说明 deviceTypes 最终还会进入构建产物的模块描述信息,而不只是 IDE 中的一个提示字段。
七、pages 和 profile:它保存的是页面清单
常见配置是:
"pages": "$profile:main_pages"
这里并没有直接写:
pages/Index
而是引用一个 profile 资源。
对应文件通常位于:
entry
└── src
└── main
└── resources
└── base
└── profile
└── main_pages.json
最小内容可以是:
{
"src": [
"pages/Index"
]
}
华为官方 HAR 转 HSP 指导给出了同样的 pages: "$profile:main_pages" 与 resources/base/profile/main_pages.json 配置,并在 src 数组中登记 pages/PageIndex。官方 ArkUI 文档也明确要求目标页面加入 main_pages.json 后才能按相应页面路径使用。
这里要分清两个动作:
module.json5
pages: "$profile:main_pages"
│
↓
resources/base/profile/main_pages.json
│
↓
登记 pages/Index
它解决的是“这个 Module 有哪些已登记页面”的问题。
但应用启动后到底加载哪个页面,还要看 UIAbility 的窗口加载逻辑。
例如官方当前代码形式:
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error(`Failed to load page, code: ${err.code}`);
return;
}
console.info('Page loaded.');
});
}
}
华为当前文档仍使用 UIAbility 配合 WindowStage.loadContent('pages/Index', ...) 加载页面。Ability Kit 的相关 Stage 模型基础接口从 API version 9 开始提供;HarmonyOS 7/API 26.0.0 环境覆盖这一版本要求。
所以,pages 和 loadContent() 也不要混为一谈:
pages/profile:页面登记
loadContent():某个 UIAbility 创建窗口后加载具体页面
八、abilities:真正注册 UIAbility 的地方
最后看:
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
]
abilities 是数组,因为一个 Module 可以配置多个 UIAbility。华为官方当前 FAQ 中就给出了在同一 abilities 数组中同时注册 EntryAbility 与 KeyboardAbility 的示例,每个 Ability 都有自己的 name 和 srcEntry。
对于本文的最小 entry 场景,我们只保留一个:
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
]
此时可以把整条关系完整连起来:
module.name = entry
│
module.type = entry
│
├── module.srcEntry
│ └── MyAbilityStage.ets(如果配置自定义 AbilityStage)
│
├── mainElement = EntryAbility
│ │
│ ↓
└── abilities[].name = EntryAbility
│
↓
abilities[].srcEntry
│
↓
EntryAbility.ets
│
↓
windowStage.loadContent()
│
↓
pages/Index
↑
│
main_pages.json 登记
如果能把这条链路建立起来,module.json5 就不再是一堆需要背诵的 JSON 字段。
九、搭一个最小可核对的 entry 配置
把前面的内容收回来,一个专门用于理解入口关系的最小示例可以组织为:
{
"module": {
"name": "entry",
"type": "entry",
"srcEntry": "./ets/myabilitystage/MyAbilityStage.ets",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
],
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
]
}
}
main_pages.json:
{
"src": [
"pages/Index"
]
}
MyAbilityStage.ets:
import { AbilityStage } from '@kit.AbilityKit';
export default class MyAbilityStage extends AbilityStage {
onCreate(): void {
console.info('MyAbilityStage onCreate');
}
}
EntryAbility.ets:
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error(`Failed to load page, code: ${err.code}`);
return;
}
console.info('Page loaded.');
});
}
}
Index.ets:
@Entry
@Component
struct Index {
build() {
Column() {
Text('module.json5 entry demo')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
这组代码按照华为官方当前的 Stage 模型配置、AbilityStage、UIAbility 与页面加载方式组织,用途是观察各层配置关系,不包含网络、文件、相机、位置等受保护能力,因此这个最小场景本身不需要额外增加 requestPermissions。实际工程仍应按照自身使用的系统能力单独核对权限。
需要说明的是,这里没有声称上述代码已经在本地 HarmonyOS 7 工程编译运行;正式使用时仍应以目标 SDK 和 DevEco Studio 的配置 Schema 检查结果为准。
十、几个特别容易理解错的地方
1. mainElement 不是 Index.ets
mainElement 找的是入口 Ability;Index.ets 是页面。入口 Ability 可以在 onWindowStageCreate() 中通过 loadContent() 加载页面。
2. 两个 srcEntry 不能只看字段名
module.srcEntry
对应 Module 级 AbilityStage。
abilities[n].srcEntry
对应具体 Ability 的实现文件。
只搜索字段名而不看 JSON 层级,很容易把两者解释成同一件事。AbilityStage 官方开发指导也明确把 Module 层 srcEntry 用作 AbilityStage 源文件路径。
3. pages 不是“首页设置”
pages 指向页面清单 profile;真正由 UIAbility 创建窗口后加载哪个页面,可以在 loadContent() 中看到。
4. deviceTypes 不是响应式布局开关
增加 "tablet" 或 "2in1" 描述的是模块支持范围。页面是否适合这些设备,仍然是另一层的 UI 适配问题。官方工程管理文档也是把 deviceTypes 定义在 Module 配置层处理。
5. 不要给 JSON 配置字段硬套 API Level
HarmonyOS 7.0 对应 API 26.0.0,这是系统/API 版本关系;UIAbility、AbilityStage 等代码能力需要检查 API 文档,而 module.json5 字段还受到当前 SDK 配置 Schema 和构建工具校验。DevEco Studio 官方 FAQ 明确展示了字段缺失、字段拼写错误时出现 Schema validate failed,并可通过 instancePath、required、propertyNames 等信息定位到具体配置。
十一、实际项目中怎么排查这条入口链
遇到“应用入口不对”“Ability 找不到”“页面没有按预期加载”一类问题时,可以沿着配置关系检查,而不是一上来修改 ArkUI 页面。
建议顺序是:先确认当前检查的是 entry/src/main/module.json5;再确认 type 是否确实为 entry;然后看 mainElement 指向的名字能否在 abilities[].name 中找到对应组件;接着检查这个 Ability 的 srcEntry 是否对应实际 ArkTS 文件;如果使用了 Module 级 srcEntry,再确认它指向的是预期 AbilityStage;最后检查 pages 引用的 profile 与 main_pages.json 页面登记,以及 loadContent() 使用的页面路径是否对应。
如果编译阶段直接出现 Schema validate failed,官方建议优先看错误中的 instancePath 和 missingProperty 等信息,而不是靠猜字段名称修改。
开发经验总结
module.json5 最适合按“Module → Ability → Page”三层关系理解,而不是逐字段死记。
name 和 type 先确定“这是哪个模块、它承担什么角色”;Module 层 srcEntry 在需要时接到 AbilityStage;mainElement 确定入口组件;abilities 注册 UIAbility,并通过各自的 srcEntry 接到具体实现;pages 再通过 $profile:main_pages 接到页面清单;最终由 UIAbility 的窗口加载逻辑决定具体加载哪个页面。
真正需要记住的,其实只有一句:
mainElement找组件,abilities[].srcEntry找组件代码,pages/profile登记页面,loadContent()才把具体页面装进 UIAbility 的窗口;而 Module 层的srcEntry是另一层——它用于指定 AbilityStage。
如果以后再看到一份复杂的 module.json5,可以先把其他字段暂时折叠,只留下本文这七组配置。入口链路看清以后,再继续理解权限、ExtensionAbility、skills 等配置,会容易很多。
❤️ 如果本文帮到了你…
- 请点个赞,让我知道你还在坚持阅读技术长文!
- 请收藏本文,因为你以后一定还会用上!
- 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
更多推荐




所有评论(0)