鸿蒙应用开发目录结构划分指导

24 阅读5分钟

一、先看全局:工程级三层架构

官方推荐将整个工程分为三个层级,各层职责分明、依赖单向:

MyApp/                          ← 工程根目录
│
├── products/                   ← ① 产品定制层(入口模块)
│   └── phone-entry/            ←   手机端 Entry HAP
│
├── features/                   ← ② 基础特性层(业务模块)
│   ├── home/                   ←   首页特性 HSP
│   ├── message/                ←   消息特性 HSP
│   └── mine/                   ←   我的特性 HSP
│
└── common/                     ← ③ 公共能力层(基础设施)
    ├── uicomponents/           ←   公共 UI 组件 HSP
    ├── network/                ←   网络封装 HSP
    └── utils/                  ←   工具类 HSP

三层职责

层级目录名职责典型内容
产品定制层products/应用入口,决定"长什么样、给谁用"Entry HAP、设备适配 UI、入口页面、专属资源
基础特性层features/独立业务功能,决定"能做什么"首页、消息、购物车、支付等独立功能模块
公共能力层common/底层基础支撑,决定"靠什么能力"公共 UI 组件、网络封装、工具类、数据管理

铁律:依赖单向向下

✅ 允许的依赖:
  products → features
  products → common
  features → common

❌ 禁止的依赖:
  common → features        (底座不能依赖上层)
  products → 跳过 features 直接依赖其内部实现
  home → message           (同层不互相依赖)

二、再看内部:模块内 MVVM 分层

每个模块(无论 HAP、HAR 还是 HSP)内部,推荐按「功能 + MVVM」组织,按功能分目录,不在同一层级混放所有 Model

features/cart/                     ← 一个功能模块
├── src/main/ets/
│   ├── model/                     ← Model 层:纯数据
│   │   ├── CartItem.ets           ←   购物车单品模型
│   │   ├── CartSummary.ets        ←   结算汇总模型
│   │   └── OrderInfo.ets          ←   订单模型
│   │
│   ├── viewmodel/                 ← ViewModel 层:业务逻辑
│   │   ├── CartViewModel.ets      ←   购物车业务逻辑
│   │   └── CheckoutViewModel.ets  ←   结算业务逻辑
│   │
│   ├── view/                      ← View 层:UI 页面与组件
│   │   ├── CartPage.ets           ←   购物车主页面
│   │   ├── CartItemCard.ets       ←   单条商品卡片
│   │   └── CartBottomBar.ets      ←   底部结算栏
│   │
│   ├── index.ets                  ← 模块对外统一出口
│   └── pages/                     ← 模块对外暴露的页面
│
├── resources/                     ← 模块专属资源
├── module.json5                   ← 模块配置
└── oh-package.json5               ← 模块依赖声明

MVVM 三层职责边界

┌─────────────────────────────────────────────┐
│  View (@ComponentV2)                        │
│  - 只做页面渲染和事件回调                     │
│  - 不写网络请求、不处理复杂业务判断            │
│  - 通过 @Param 接收数据,通过 @Event 上报事件  │
└──────────────┬──────────────────────────────┘
               │  事件回调 ↑  ↓ 数据绑定
┌──────────────┴──────────────────────────────┐
│  ViewModel (@ObservedV2)                    │
│  - 持有 Model,处理所有业务逻辑               │
│  - 接口调用、状态管理、数据转换               │
│  - 加载/错误/空状态管理                      │
└──────────────┬──────────────────────────────┘
               │  读写
┌──────────────┴──────────────────────────────┐
│  Model (@ObservedV2 + @Trace)               │
│  - 纯数据结构,不依赖 UI                     │
│  - @Trace 字段变化自动通知 View 刷新          │
│  - 可包含 @Computed 计算属性                 │
└─────────────────────────────────────────────┘

三、完整落地示例

整体目录(中型项目)

MyApp/
├── AppScope/                             ← 应用全局配置
│   └── app.json5                         ←   应用包名、版本、图标
│
├── products/                             ← 产品定制层
│   └── phone-entry/                      ←   手机端入口 Entry HAP
│       ├── src/main/ets/
│       │   ├── entryability/             ←   UIAbility 入口
│       │   ├── pages/                    ←   入口页面
│       │   └── ...
│       ├── resources/
│       └── module.json5
│
├── features/                             ← 基础特性层
│   ├── home/                             ←   首页 HSP
│   │   ├── src/main/ets/
│   │   │   ├── model/                    ←   首页数据模型
│   │   │   ├── viewmodel/               ←   首页业务逻辑
│   │   │   ├── view/                     ←   首页 UI
│   │   │   ├── pages/                    ←   首页对外页面
│   │   │   └── index.ets                 ←   统一导出
│   │   └── ...
│   ├── search/                           ←   搜索 HSP
│   │   └── src/main/ets/
│   │       ├── model/
│   │       ├── viewmodel/
│   │       └── view/
│   └── mine/                             ←   我的 HSP
│       └── src/main/ets/
│           ├── model/
│           ├── viewmodel/
│           └── view/
│
├── common/                               ← 公共能力层
│   ├── uicomponents/                     ←   公共 UI 组件 HSP
│   │   └── src/main/ets/
│   │       ├── components/               ←     按钮、卡片等
│   │       └── index.ets
│   ├── network/                          ←   网络封装 HSP
│   │   └── src/main/ets/
│   │       ├── HttpClient.ets            ←     请求封装
│   │       ├── ApiService.ets            ←     接口定义
│   │       └── index.ets
│   └── utils/                            ←   工具类 HSP
│       └── src/main/ets/
│           ├── DateUtils.ets
│           ├── StringUtils.ets
│           └── index.ets
│
├── hvigor/                               ← 构建配置
├── oh-package.json5                      ← 工程级依赖
└── build-profile.json5                   ← 构建配置

模块对外暴露:index.ets 统一出口

每个模块通过 index.ets 控制对外暴露的接口,上层模块通过 oh-package.json5 声明依赖:

// common/uicomponents/index.ets — 公共组件层统一出口
export { SectionCard } from './src/main/ets/components/SectionCard'
export { LoadingView } from './src/main/ets/components/LoadingView'
export { EmptyView } from './src/main/ets/components/EmptyView'

// features/cart/index.ets — 购物车模块统一出口
export { CartViewModel } from './src/main/ets/viewmodel/CartViewModel'
export { CartPage } from './src/main/ets/view/CartPage'
// 不导出内部实现细节:CartItemCard、CartBottomBar
// features/home/oh-package.json5 — 首页模块的依赖声明
{
  "dependencies": {
    "@hm/common": "file:../../common"    // 只依赖公共层,不依赖同层的 cart
  }
}

四、不同规模项目的目录策略

🟢 小型项目(3~5 个页面,单人开发)

不需要拆 HAR/HSP 模块,在单 Entry 内按 MVVM 分层即可:

entry/src/main/ets/
├── pages/               ← 页面(View)
│   ├── IndexPage.ets
│   ├── DetailPage.ets
│   └── SettingPage.ets
├── viewmodel/           ← 业务逻辑(ViewModel)
│   ├── IndexViewModel.ets
│   └── DetailViewModel.ets
├── model/               ← 数据模型(Model)
│   ├── ArticleItem.ets
│   └── UserInfo.ets
├── common/              ← 公共工具
│   ├── http/
│   ├── utils/
│   └── components/
└── entryability/

🟡 中型项目(10~20 个页面,团队协作)

引入模块化,按功能拆分 HAR 或 HSP:

entry/src/main/ets/           ← 主入口,薄薄一层
├── pages/
└── entryability/

features/
├── home/      (HSP)          ← 首页模块
├── discover/  (HSP)          ← 发现模块
├── cart/      (HSP)          ← 购物车模块
└── mine/      (HSP)          ← 个人中心模块

common/
├── uicomponents/ (HSP)       ← 公共 UI
├── network/      (HSP)       ← 网络封装
└── utils/        (HSP)       ← 工具类

🔴 大型项目(多端 + 多团队)

全面启用三层架构 + Feature HAP 按需下载:

products/
├── phone-entry/   (Entry HAP)
├── tablet-entry/  (Entry HAP)
└── tv-entry/      (Entry HAP)

features/
├── home/          (HSP — 各端共用)
├── live/          (Feature HAP — 仅 TV 端按需下载)
├── payment/       (HSP)
└── chat/          (Feature HAP — 按需下载,deliveryWithInstall: false)

common/
├── uicomponents/  (HSP)
├── network/       (HSP)
├── database/      (HSP)
└── analytics/     (HSP)

五、常见反模式(务必避免)

反模式问题正确做法
models/viewmodels/views/ 三个大目录平铺所有文件改一个功能要在三个目录间反复跳,模块边界模糊按功能模块分目录,每个模块内部再分 model/viewmodel/view
公共层 common 里写了业务逻辑底座与业务耦合,换个项目用不了common 只放无业务属性的基础设施
页面里直接写网络请求和数据解析代码不可测试、不可复用网络请求放 ViewModel,页面只负责渲染
同层模块互相 import形成依赖环,编译可能报错,逻辑耦合共享部分下沉到 common
一个 Entry 里堆了所有业务代码无法做按需下载,编译慢,团队冲突大按功能拆成 HAR/HSP 模块

六、总结:一张图记住

工程目录 = 三层架构(横向) × MVVM 分层(纵向)

                    ┌──────────────────┐
                    │   products/      │ ← 入口 + 设备适配
                    │   (Entry HAP)    │
                    ├──────────────────┤
  依赖单向向下 →     │   features/      │ ← 独立业务模块
                    │   (HSP / HAR)    │
                    ├──────────────────┤
                    │   common/        │ ← 公共基础设施
                    │   (HSP / HAR)    │
                    └──────────────────┘

每个模块内部 = model/ + viewmodel/ + view/ + index.ets

记住两个原则:

  1. 横向分层:products → features → common,依赖只向下。
  2. 纵向分块:每个 feature 内部 model / viewmodel / view 各司其职,通过 index.ets 对外暴露。