第三章 元服务项目结构:工程目录解析
好,咱们今天聊聊元服务的项目结构。说实话,我刚从传统App开发转到鸿蒙时,第一眼看到这个目录结构,心里是有点懵的。怎么多了这么多文件夹?但用顺手之后,你会发现——这结构设计得真挺讲究。
3.1 工程目录概览:entry、feature、library
一个标准的元服务工程,通常包含三个核心模块:entry、feature、library。我习惯把它们比作「入口、功能、底座」三层。
| 模块 | 作用 | 我常用的场景 |
|---|---|---|
| entry | 应用的主入口,负责启动和基础配置 | 放首页、路由配置、全局初始化 |
| feature | 业务功能模块,按功能拆分 | 登录、支付、扫码等独立业务 |
| library | 公共基础库,被其他模块依赖 | 网络请求、工具类、自定义组件 |
为什么要拆这么细?说白了,就是为了「解耦」。我在项目中遇到过好几次,所有代码都堆在entry里,后期改一个支付逻辑,结果首页也跟着报错。那叫一个头疼。
我的建议:哪怕你的元服务只有三个页面,也请至少拆出entry和library两层。这是为未来扩展留的「后路」。
3.2 entry模块详解
entry是元服务的「门面」。你想想看,用户点开你的服务,第一个加载的就是entry里的代码。
它的典型结构是这样的:
entry/
├── src/
│ ├── main/
│ │ ├── ets/ # 页面逻辑
│ │ ├── resources/ # 资源文件
│ │ └── module.json5 # 模块配置
│ └── ohosTest/ # 测试代码
├── build-profile.json5 # 构建配置
└── package.json # 依赖管理
嗯,这里要注意:entry模块只能有一个。每个元服务工程有且仅有一个entry,它负责声明Ability、配置路由、初始化全局变量。
小技巧:我习惯在entry的ets目录下建一个pages/文件夹,专门放首页和启动页。其他业务页面,全部放到feature模块里。这样entry永远保持「轻量」。
3.3 feature模块:业务功能的「收纳盒」
feature模块,说白了就是放具体业务的地方。你可以按功能拆成多个feature,比如:
feature_login— 登录注册feature_payment— 支付流程feature_scan— 扫码功能
每个feature都是一个独立的HAP包。为什么要独立?因为元服务支持「按需加载」。用户可能只用到了扫码功能,那登录模块的代码就不需要提前下载。这能显著减少首包体积。
我曾经做过一个实验:把三个业务拆成独立feature后,首包体积从2.3MB降到了800KB。用户打开速度提升了近一倍。
避坑指南:feature之间不要互相依赖!如果feature_a引用了feature_b的代码,那它们就「耦合」了。正确的做法是:把公共代码下沉到library,feature只依赖library。
3.4 library模块:代码的「公共仓库」
library是纯代码库,不包含任何Ability。它专门存放:
- 网络请求封装(比如axios或http)
- 工具类(日期格式化、字符串处理)
- 自定义UI组件(按钮、弹窗、卡片)
- 数据模型(DTO、VO)
它的目录结构通常长这样:
library/
├── src/
│ └── main/
│ ├── ets/
│ │ ├── components/ # 公共组件
│ │ ├── utils/ # 工具函数
│ │ └── model/ # 数据模型
│ └── resources/ # 公共资源
└── index.ets # 导出入口
我个人习惯在library里放一个index.ets文件,统一导出所有公共模块。这样其他模块引用时,只需要写一行:
import { HttpUtil, DateUtil, MyButton } from '@ohos/library';
是不是清爽多了?
3.5 资源文件管理(resources)
资源文件这块,很多新手容易搞乱。鸿蒙的资源管理其实很「讲究」——它支持多语言、多设备、多屏幕密度。
resources目录的标准结构:
resources/
├── base/ # 基础资源(默认)
│ ├── element/ # 字符串、颜色、尺寸
│ ├── media/ # 图片、音频
│ └── profile/ # 配置文件
├── en_US/ # 英文资源
├── zh_CN/ # 中文资源
└── rawfile/ # 原始文件(不压缩)
举个例子,你在base/element/string.json里定义:
{
"string": [
{ "name": "app_name", "value": "我的元服务" }
]
}
然后在zh_CN/element/string.json里覆盖:
{
"string": [
{ "name": "app_name", "value": "我的元服务(中文版)" }
]
}
系统会自动根据用户语言加载对应的资源。嗯,这个机制在出海场景下特别有用。
我的经验:图片资源尽量用SVG格式。同样是图标,PNG可能几十KB,SVG只有几KB。而且SVG支持主题色切换,用户体验更好。
3.6 module.json5配置文件详解
这个文件是每个模块的「身份证」。它告诉系统:这个模块叫什么、有哪些Ability、需要什么权限。
一个典型的module.json5长这样:
{
"module": {
"name": "entry",
"type": "entry",
"description": "元服务主入口",
"mainElement": "MainAbility",
"deviceTypes": ["phone", "tablet"],
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/MainAbility/MainAbility.ets",
"description": "主Ability",
"icon": "$media:icon",
"label": "$string:app_name",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"visible": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
这里有几个关键字段,我重点说一下:
- type:可选
entry、feature、shared。entry只能有一个,feature可以有多个。 - deviceTypes:支持的设备类型。我建议至少写上
phone和tablet,因为元服务在平板上体验也很好。 - abilities:每个Ability相当于一个「页面入口」。如果你有多个页面需要独立启动,就声明多个Ability。
- skills:这个决定了用户怎么打开你的服务。比如
entity.system.home表示可以从桌面图标打开。
避坑指南:我曾经犯过一个错——忘记在requestPermissions里声明网络权限。结果上线后,用户反馈「怎么加载不出数据?」排查了半天,原来是权限没配。所以,每次写完配置,记得检查一遍权限列表。
3.7 总结:目录结构的最佳实践
说了这么多,我总结一下我个人最推荐的工程结构:
MyService/
├── entry/ # 主入口(轻量)
│ ├── src/main/ets/pages/ # 只放首页和启动页
│ └── module.json5
├── feature_login/ # 登录模块
├── feature_payment/ # 支付模块
├── feature_scan/ # 扫码模块
├── library/ # 公共库
│ ├── components/ # 公共组件
│ ├── utils/ # 工具类
│ └── model/ # 数据模型
└── build-profile.json5 # 全局构建配置
这种结构的好处是:
- entry保持「瘦身」,启动速度快
- feature按业务拆分,方便团队协作
- library统一管理,避免代码重复
你想想看,如果一开始就按这个结构来,后期加功能、修bug、做国际化,是不是都轻松很多?
好,这一章就到这里。下一章咱们聊聊页面布局与组件开发——那可是元服务UI的「灵魂」所在。