Skip to content

10_Engineering/04 - 模块化架构

1. 为什么需要模块化

HarmonyOS 大型应用(如金融、电商、社交类应用)往往包含数十个功能模块。没有模块化的代码库会导致:

  • 编译时间过长(全量编译 vs 增量编译)
  • 代码耦合严重,修改一处影响多处
  • 团队协作困难,合并冲突频繁
  • 无法独立测试和部署子功能
  • 不同团队重复造轮子

2. 模块化策略

2.1 分层架构

┌─────────────────────────────────────────┐
│           Entry (应用层 / UI层)            │
│  Index.ets | LoginPage.ets | Home.ets   │
├─────────────────────────────────────────┤
│       Feature (业务逻辑层 / Feature模块)     │
│  network/ │ pay/ │ search/ │ cart/     │
├─────────────────────────────────────────┤
│        Common (公共层 / 共享库)             │
│  common/  │ ui-kit/ │ logger/          │
├─────────────────────────────────────────┤
│          Kernel (内核层 / 基础库)            │
│  crypto/  │ cache/ │ config/           │
└─────────────────────────────────────────┘

2.2 模块化原则

原则说明
单一职责每个模块只负责一个业务域
高内聚低耦合模块内部紧密,模块之间松散
依赖方向上层依赖下层,不允许反向依赖
独立编译每个模块可独立编译、独立测试
接口隔离通过 ArkTS 接口(interface)暴露能力

3. 组件化架构

3.1 组件化 vs 模块化

维度模块化(Module)组件化(Component)
粒度模块级(HAR/HSP)组件级(独立可复用单元)
编译Hvigor 多模块可独立 App 或库
耦合通过依赖引用通过接口/路由
测试模块内测试组件可独立测试
复用编译时复用运行时复用 + 编译时复用
适用规模中小项目大型项目、多产品线

3.2 组件架构示例

MyApp/
├── entry/                    # Entry HAP - 主应用壳
├── feature-home/             # HAR - 首页模块
│   ├── src/main/ets/
│   │   ├── HomePage.ets
│   │   ├── HomeViewModel.ets
│   │   └── HomeRouter.ets  # 模块路由
├── feature-search/           # HAR - 搜索模块
├── feature-cart/             # HAR - 购物车模块
├── feature-pay/              # HSP - 支付模块(需动态加载)
├── common-core/              # HAR - 核心公共库
├── common-ui/                # HSP - UI 组件库
├── network/                  # HAR - 网络层
└── crypto/                   # HAR - 加密模块

3.3 模块间通信

方式一:接口依赖(推荐)

typescript
// common-ui/src/main/ets/interfaces/IButton.ets
export interface IButton {
  onClick(): void;
  setText(text: string): void;
}

// feature-home/src/main/ets/HomePage.ets
import { IButton } from '@ohos/common-ui';

@Component
struct HomePage {
  @State message: string = '';

  build() {
    Column() {
      MyButton({
        onClick: () => { this.message = 'Hello'; },
        text: 'Click Me'
      })
    }
  }
}

方式二:路由分发(组件解耦)

typescript
// common-core/src/main/ets/router/RouteManager.ets
import { router } from '@kit.ArkUI';

export class RouteManager {
  static navigate(page: string, params?: object): void {
    switch (page) {
      case 'search':
        router.pushUrl({ url: 'feature-search:SearchPage', params });
        break;
      case 'cart':
        router.pushUrl({ url: 'feature-cart:CartPage', params });
        break;
      case 'pay':
        router.pushUrl({ url: 'feature-pay:PayPage', params });
        break;
    }
  }
}

// feature-home 中调用
RouteManager.navigate('search', { keyword: '手机' });

方式三:事件总线(跨模块通信)

typescript
// common-core/src/main/ets/event/EventBus.ets
export class EventBus {
  private static instance: EventBus | null = null;
  private listeners: Map<string, Function[]> = new Map();

  static getInstance(): EventBus {
    if (!this.instance) {
      this.instance = new EventBus();
    }
    return this.instance;
  }

  on(event: string, callback: Function): void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event)!.push(callback);
  }

  emit(event: string, data?: any): void {
    const cbs = this.listeners.get(event) || [];
    cbs.forEach(cb => cb(data));
  }
}

// 模块 A 监听
EventBus.getInstance().on('orderPlaced', (order: Order) => {
  console.info('收到订单通知:', order.id);
});

// 模块 B 发送
EventBus.getInstance().emit('orderPlaced', order);

4. 多模块管理

4.1 依赖管理

json5
// oh-package.json5
{
  "dependencies": {
    "feature-home": { "path": "./feature-home" },
    "feature-search": { "path": "./feature-search" },
    "feature-pay": { "path": "./feature-pay" },
    "common-core": { "path": "./common-core" },
    "common-ui": { "path": "./common-ui" },
    "network": { "path": "./network" }
  }
}

4.2 构建配置

json5
// build-profile.json5
{
  "modules": [
    {
      "name": "entry",
      "srcPath": "./entry",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }]
    },
    {
      "name": "feature-home",
      "srcPath": "./feature-home",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }]
    },
    {
      "name": "feature-search",
      "srcPath": "./feature-search",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }]
    },
    {
      "name": "common-core",
      "srcPath": "./common-core",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }]
    },
    {
      "name": "common-ui",
      "srcPath": "./common-ui",
      "type": "shared",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }]
    }
  ]
}

4.3 模块职责分离表

模块类型职责对外接口
feature-homeHAR首页业务逻辑HomePage, HomeViewModel
feature-searchHAR搜索业务逻辑SearchPage, SearchViewModel
feature-payHSP (dynamicLoad)支付业务逻辑PayPage, PayService
common-coreHAR路由、事件、常量RouteManager, EventBus, Constants
common-uiHSP (dynamicShare)UI 组件库IButton, Input, Card 等
networkHARHTTP 请求封装HttpClient, ApiResponse
cryptoHAR加密、签名CryptoUtils, Encryptor

5. 模块化最佳实践

5.1 目录规范

feature-home/
├── src/main/
│   ├── ets/
│   │   ├── HomePage.ets
│   │   ├── HomeViewModel.ets
│   │   ├── model/
│   │   │   └── BannerItem.ets
│   │   └── api/
│   │       └── HomeApi.ets
│   ├── resources/
│   └── module.json5
├── hvigorfile.ts
├── build-profile.json5
└── README.md              # 模块使用说明

5.2 接口定义规范

typescript
// feature-home/src/main/ets/api/IHomeApi.ets
/** 首页数据接口 */
export interface IHomeApi {
  /** 获取首页数据 */
  getHomeData(): Promise<HomeData>;
  /** 获取推荐列表 */
  getRecommendList(page: number): Promise<RecommendItem[]>;
  /** 下拉刷新 */
  refresh(): Promise<void>;
}

// 实现
export class HomeApiImpl implements IHomeApi {
  async getHomeData(): Promise<HomeData> {
    const response = await Network.get('/api/home');
    return response.data as HomeData;
  }

  async getRecommendList(page: number): Promise<RecommendItem[]> {
    const response = await Network.get('/api/recommend', { page });
    return response.data as RecommendItem[];
  }

  async refresh(): Promise<void> {
    // refresh logic
  }
}

6. 面试高频考点

Q1: 模块化和组件化有什么区别?

回答要点

  • 模块化是编译时概念(HAR/HSP 模块)
  • 组件化是架构设计概念,模块是组件化的实现手段
  • 组件化更强调独立、可替换、可复用
  • 组件通过接口/路由解耦,模块通过依赖管理

Q2: 多模块间如何通信?

回答要点

  • 接口依赖:通过 ArkTS interface 定义,编译期类型检查
  • 路由分发:通过 RouteManager 统一路由,降低耦合
  • 事件总线:跨模块发布/订阅模式
  • 推荐优先使用接口依赖,其次路由,最后事件总线

Q3: 如何防止模块间循环依赖?

回答要点

  • Hvigor 在编译期会检测循环依赖并报错
  • 使用接口隔离:A 依赖 B 的 interface,B 不依赖 A
  • 将共享接口提取到独立的 core 模块
  • 代码审查 + 静态检查工具

7. Android 对比

概念AndroidHarmonyOS
模块化Gradle 多 moduleHvigor 多 module
静态库.aarHAR
动态库N/A(通过 Dynamic Feature)HSP
组件通信Interface / EventBus / RoomInterface / EventBus / 路由
依赖管理Gradle / Maven / JitPackoh-package.json5 / ArkPackage