1. 鸿蒙模块化开发的核心价值

第一次接触鸿蒙模块化开发时,我被它优雅的工程结构设计惊艳到了。相比传统开发中常见的"大杂烩"式代码堆砌,鸿蒙的模块化体系让项目维护变得异常清晰。想象一下你的代码库就像一个精心设计的工具箱,每个工具都有专属位置,需要时随手取用,这就是模块化带来的最直观好处。

在大型商业项目实战中,我遇到过代码量超过10万行的鸿蒙应用。如果没有采用模块化架构,光是理清各个功能之间的调用关系就会让人崩溃。通过将系统拆分为entry主模块、多个feature功能模块和共享library,我们实现了:

  • 功能解耦:每个团队负责独立模块开发
  • 并行开发:模块间通过明确定义的接口协作
  • 热更新能力:可以单独更新某个功能模块

最让我印象深刻的是跨工程复用能力。去年我们开发企业级应用时,成功将用户认证模块复用到三个不同项目中,节省了约40%的开发时间。这种复用不是简单的代码拷贝,而是通过.har静态共享包实现的真正工程级复用。

2. 跨工程依赖的三种实现路径

2.1 源码级依赖(开发阶段首选)

在开发调试阶段,直接引用模块源码是最灵活的方式。假设我们有两个独立工程ProjectA和ProjectB,需要共享一个网络请求模块:

// ProjectA/entry/oh-package.json5
{
  "dependencies": {
    "network": "file:../../ProjectB/network"
  }
}

关键配置要点:

  1. 使用file:协议指定相对路径
  2. 路径要指向模块根目录(包含oh-package.json5的目录)
  3. 被依赖模块需要在module.json5中声明exported组件

实际项目中我推荐创建一个local_modules目录集中管理跨工程模块,这样路径引用会更清晰:

company_projects/
├── ProjectA/
├── ProjectB/
└── local_modules/
    ├── network/
    └── common_utils/

2.2 HAR包依赖(生产环境推荐)

当项目进入稳定阶段,使用.har静态共享包是更安全的选择。生成har包时有个实用技巧:

# 在模块目录执行构建
npm run build -- --mode production --shrink true

这个命令会:

  • 启用生产模式(压缩代码)
  • 开启混淆(shrink参数)
  • 生成带版本号的har包(如network-1.0.0.har)

跨工程引用时,建议使用绝对路径避免移动工程导致的路径失效:

// 在Windows系统
"dependencies": {
  "network": "file:D:/harmony_modules/network-1.0.0.har"
}

// 在Mac系统  
"dependencies": {
  "network": "file:/Users/name/harmony_modules/network-1.0.0.har"
}

2.3 私有npm仓库(企业级方案)

对于大型团队,搭建私有npm仓库是终极解决方案。我们公司使用Verdaccio搭建的内部仓库,配置方法:

  1. 发布模块到私有仓库:
npm config set registry http://内部仓库地址
npm login
npm publish
  1. 工程中引用:
"dependencies": {
  "@company/network": "^1.0.0"
}

这种方式的优势在于版本管理自动化,配合CI/CD可以实现依赖自动更新。

3. 依赖冲突的实战解决方案

在同时引入多个模块时,很容易遇到依赖冲突。上周我就处理了一个典型case:主工程依赖moduleA和moduleB,而这两个模块又分别依赖了不同版本的utils模块。

3.1 版本冲突检测

首先用这个命令查看依赖树:

npm list --depth=3

输出会显示冲突点:

├─┬ moduleA@1.0.0
│ └── utils@2.1.0 
└─┬ moduleB@1.2.0
  └── utils@3.0.0

3.2 解决方案

方案一:强制统一版本 在根工程oh-package.json5中添加:

"resolutions": {
  "utils": "3.0.0"
}

这会强制所有子模块使用指定版本,但需要测试兼容性。

方案二:代码隔离 修改moduleB的引用方式:

import { utils } from 'moduleB/utils-wrapper';

然后在moduleB中创建中间层封装utils调用。

方案三:动态加载 对于非关键依赖,可以改用动态导入:

const utils = await import('moduleB/src/main/ets/utils');

4. 性能优化实战技巧

4.1 按需加载策略

在开发资讯类应用时,我们将视频播放器做成了动态模块。关键实现:

// 在页面中
Button('播放视频')
  .onClick(async () => {
    const videoModule = await import('video_player');
    videoModule.showPlayer(this.videoUrl);
  })

配置模块为异步加载:

// module.json5
{
  "module": {
    "name": "video_player",
    "deliveryWithInstall": false
  }
}

实测这种方案使应用启动时间减少了35%。

4.2 资源优化

跨模块资源引用容易造成重复打包。我们的解决方案:

  1. 创建resources模块集中管理公共资源
  2. 其他模块通过软链接引用:
# 在模块resources目录执行
ln -s ../../common_resources/base base
  1. 在代码中统一引用:
Image($r('common.media.logo'))

4.3 编译加速技巧

大型项目编译耗时是个痛点,我们通过以下配置将编译速度提升50%:

  1. 在根工程oh-package.json5中添加:
"harmony": {
  "buildMode": "incremental",
  "cache": true
}
  1. 对稳定模块设置预编译:
npm run build -- --prebuilt

5. 调试与排错指南

5.1 依赖关系可视化

使用这个命令生成依赖图:

npm install -g harmony-dep-graph
harmony-dep-graph --output=graph.html

生成的HTML文件会直观展示模块间的依赖关系,特别适合排查循环依赖问题。

5.2 真机调试技巧

当跨工程引用出现问题时,可以这样检查:

// 打印所有已加载模块
console.log(require.context.keys());

// 检查特定模块导出内容
const moduleAPI = require.context('network');
console.log(Object.keys(moduleAPI));

5.3 常见错误处理

错误1:Module not found

  • 检查oh-package.json5路径是否正确
  • 确认被依赖模块已添加到工程配置
  • 尝试删除node_modules重新安装

错误2:Resource conflict

  • 使用完整资源路径:$r('module.media.icon')
  • 在冲突模块的oh-package.json5中添加别名:
"harmony": {
  "resourceAlias": {
    "icon": "module_icon"
  }
}

在多个商业项目实战中,这些技巧帮助我们节省了大量调试时间。特别是资源冲突问题,通过建立命名规范和完善的文档记录,现在团队已经很少遇到这类问题了。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐