HarmonyOS 依赖库精简深度实战——从钻石依赖治理到 HSP 动态共享的全链路优化方案
文章目录

每日一句正能量
求木之长者,必固其根本;欲流之远者,必浚其泉源。
无论是个人成长、事业建设还是关系经营,追逐枝叶的繁茂(速成、表象)不如深耕根本(能力、品德、健康);想要源远流长,就必须疏通源头(初心、动机、系统)。一切长久的美好,都离不开扎实的基础。
一、前言:依赖库是应用体积的"沉默膨胀器"
在 HarmonyOS 应用开发中,依赖库的管理是一个容易被忽视但影响深远的环节。随着业务迭代,项目中的依赖数量会不断增长——三方 SDK、内部工具库、UI 组件库、网络请求库、数据存储库……每一个依赖的引入都看似微不足道,但累积起来却能让应用的包体积膨胀到难以控制的程度。
更隐蔽的是"钻石依赖"问题:当多个模块依赖同一个库的不同版本时,包管理器(OHPM)默认选择最近版本,这可能导致编译失败或运行时崩溃。而对于包含 Native 代码(C++)的依赖,版本不兼容会直接引发符号表错误,造成应用启动即崩溃。
HarmonyOS 提供了完整的依赖治理工具链:从 ohpm list 查看依赖树、ohpm prune 清理未使用依赖、overrides 统一版本,到 deduplicateHar 自动去重、HAR→HSP 动态共享替换。本文将系统讲解这些技术的原理、配置方法和实战技巧,帮助开发者实现依赖体积减少 45%、整体包体积减少 35% 的目标。
二、HarmonyOS 依赖库精简技术全景图
HarmonyOS 的依赖库精简可分为三大维度:依赖去重、版本治理、按需加载,每个维度都有对应的官方工具支持。

| 优化维度 | 核心技术 | 工具支持 | 预期效果 |
|---|---|---|---|
| 依赖去重 | ohpm 依赖去重、HAR→HSP 替换、deduplicateHar | app-check-tool、Build Analyzer | 减少 30-50% 重复依赖体积 |
| 版本治理 | overrides 统一版本、resolutionStrictness、钻石依赖消除 | ohpm list、ohpm outdated | 消除版本冲突,提升编译稳定性 |
| 按需加载 | Feature HAP 拆分、动态 import、延迟初始化 | hvigor、app-check-tool | 减少 60-70% 初始包体积 |
三、钻石依赖问题与 overrides 版本统一
3.1 钻石依赖问题的本质
钻石依赖(Diamond Dependency)是多模块工程中最常见的问题之一。当两个不同的模块分别依赖同一个库的不同版本时,包管理器需要决定使用哪个版本——这个决定可能引发连锁反应。

典型场景:
MyApp
├── Entry HAP → @ohos/aki@1.0.0
├── Feature Pay HAP → @ohos/aki@1.1.0
└── Feature Chat HAP → @ohos/aki@1.0.0
在这个场景中:
- Entry 和 Feature Chat 依赖
aki@1.0.0 - Feature Pay 依赖
aki@1.1.0 - OHPM 默认选择最近版本(1.1.0)
- 但
aki@1.1.0的 Native 符号表与aki@1.0.0不兼容 - 结果:Entry 和 Feature Chat 在调用
aki的 Native 方法时发生符号解析失败,应用崩溃
3.2 overrides:强制统一依赖版本
overrides 是 OHPM 提供的版本覆盖机制,可以在工程级 oh-package.json5 中强制指定所有模块(包括传递依赖)使用的版本。
// 工程级 oh-package.json5(项目根目录)
{
"name": "MyHarmonyApp",
"version": "1.0.0",
"description": "示例 HarmonyOS 应用",
// overrides: 强制所有模块使用指定版本
"overrides": {
"@ohos/aki": "1.1.0", // 统一使用 1.1.0
"@ohos/net": "1.2.0", // 统一使用 1.2.0
"@ohos/crypto": "2.0.1", // 统一使用 2.0.1
"@ohos/file.picker": "1.0.5" // 统一使用 1.0.5
},
// strict 模式:强制严格匹配,版本不一致时直接报错
"resolutionStrictness": "strict"
}
overrides 的优先级规则:
- 最高优先级:
overrides中的声明 - 次优先级:模块级
dependencies中的声明 - 最低优先级:传递依赖的默认版本
strict 模式的作用:
# 若某个模块的 dependencies 中声明了与 overrides 不一致的版本
# strict 模式下 OHPM 会直接报错,而不是静默覆盖
# 示例报错信息:
# ERROR: Module "feature_pay" depends on @ohos/aki@1.0.0,
# but overrides requires @ohos/aki@1.1.0.
# Please update the dependency version.
3.3 本地包替换临时修复
当某个三方库存在 bug 但官方尚未修复时,可以通过 overrides 指定本地修改后的版本:
{
"overrides": {
"@ohos/file.photoPicker": "file:./local_patches/photoPicker_fixed"
}
}
local_patches/
└── photoPicker_fixed/
├── oh-package.json5
├── index.ets
└── ...
注意事项:
overrides仅在工程级oh-package.json5中生效- 修改
overrides后需要重新执行ohpm install - 本地包替换仅适用于临时修复,长期应推动官方修复
四、HAR → HSP:消除重复代码拷贝
4.1 HAR 与 HSP 的本质差异
| 特性 | HAR(静态共享包) | HSP(动态共享包) |
|---|---|---|
| 复用时机 | 编译时(代码复制到每个模块) | 运行时(动态加载,进程中仅一份) |
| 包体积影响 | 增加(重复代码) | 减少(共享代码) |
| 编译产物 | 每个模块独立包含 HAR 代码 | HSP 独立打包,模块仅保留引用 |
| 适用场景 | 三方库分发、独立工具类 | 应用内多模块共享代码 |

以一个包含 Entry HAP + 3 个 Feature HAP 的工程为例,若 utils.har(100KB)、network.har(200KB)和 chart.har(300KB)被多个模块引用:
- 使用 HAR:总包体积 = 各模块自身代码 + utils×3 + network×3 + chart×3 = 3000KB
- 使用 HSP:总包体积 = 各模块自身代码 + utils×1 + network×1 + chart×1 = 1800KB
- 体积减少:1200KB(40%)
4.2 HAR → HSP 替换实战
步骤一:创建 HSP 模块
// shared_utils/module.json5
{
"module": {
"name": "shared_utils",
"type": "shared",
"description": "公共工具类动态共享包",
"mainElement": "SharedUtilsAbility",
"abilities": [
{
"name": "SharedUtilsAbility",
"srcEntry": "./ets/utilsability/UtilsAbility.ets"
}
]
}
}
步骤二:修改各模块的依赖引用
// entry/oh-package.json5
{
"dependencies": {
// 原 HAR 依赖(编译时拷贝)
// "@myapp/utils": "file:./utils.har"
// 改为 HSP 依赖(运行时共享)
"@myapp/utils": "file:./shared_utils"
}
}
步骤三:HSP 混淆白名单配置
由于 HAP 和 HSP 是独立编译的,混淆后导出名称可能不一致,需要配置白名单:
// shared_utils/consumer-rules.txt
-keep-global-name
formatDate
parseUrl
deepClone
NetworkManager
StorageManager
// shared_utils/obfuscation-rules.txt
-keep-global-name
formatDate
parseUrl
deepClone
NetworkManager
StorageManager
4.3 DevEco Studio 6.0+ 自动去重
从 DevEco Studio 6.0.1 Beta1 开始,支持在构建 APP/HAP/HSP 时自动去除 HSP 中重复的 HAR:
// 工程级 build-profile.json5
{
"apiType": "stageMode",
"buildOption": {
"packOptions": {
"deduplicateHar": true // 去除 HSP 中重复的 HAR
}
},
"useNormalizedOHMUrl": true
}
效果:当多个 HSP 引用了同一个 HAR 时,构建工具会自动去重,确保最终包中该 HAR 仅存在一份。
五、ohpm 依赖分析工具链
HarmonyOS 提供了完整的依赖分析工具链,帮助开发者全面了解项目的依赖状况。

5.1 ohpm list:查看完整依赖树
# 查看当前模块的依赖树(包含传递依赖)
ohpm list --depth=3
# 输出示例:
# MyHarmonyApp
# ├── @ohos/aki@1.1.0
# │ └── @ohos/crypto@2.0.1
# ├── @ohos/net@1.2.0
# │ ├── @ohos/aki@1.1.0 (dedup)
# │ └── @ohos/utils@1.0.0
# ├── @ohos/chart@3.0.0
# │ └── @ohos/aki@1.1.0 (dedup)
# └── @ohos/file.picker@1.0.5
# 查找特定依赖的所有版本
ohpm list --depth=3 | grep "@ohos/aki"
# 查看依赖树并标记重复项
ohpm list --depth=3 --duplicates
5.2 ohpm prune:清理未使用依赖
# 清理当前模块中未使用的依赖
ohpm prune
# 清理所有模块的未使用依赖
ohpm prune --all
# 清理并更新 oh-package-lock.json5
ohpm prune --update
# 清理并显示详细信息
ohpm prune --verbose
注意事项:
ohpm prune基于oh-package-lock.json5分析依赖使用关系- 清理前建议备份
oh-package-lock.json5 - 清理后需要重新构建验证功能完整性
5.3 ohpm outdated:检测过期依赖
# 检测所有过期依赖
ohpm outdated
# 输出示例:
# Package Current Wanted Latest
# @ohos/net 1.1.0 1.2.0 1.2.0
# @ohos/crypto 1.5.0 2.0.1 2.0.1
# @ohos/chart 2.5.0 3.0.0 3.0.0
# 导出 JSON 格式报告
ohpm outdated --json > outdated-report.json
# 仅检测安全更新
ohpm outdated --security
5.4 --analyze:编译性能分析
# 生成编译性能依赖图
hvigorw assembleRelease --analyze
# 分析结果保存在 build/reports/analyze/
# 包含:
# - 各模块编译耗时
# - 依赖解析耗时
# - 循环依赖检测
# - 冗余依赖警告
5.5 app-check-tool:重复依赖扫描
# 扫描 HAP/HSP 包中的重复 HAR
java -jar $OHOS_SDK/toolchains/lib/app-check-tool.jar \
--mode hap \
--input build/outputs/default/packaging/entry-default-signed.hap \
--output ./scan-report/
# 扫描结果中的重复依赖示例:
# {
# "duplicateAnalysis": [
# {
# "fileName": "libnetwork.so",
# "occurrences": 4,
# "wastedSize": 25794972,
# "suggestion": "建议将包含 libnetwork.so 的 HAR 包改为 HSP 动态共享包"
# }
# ]
# }
六、传递依赖优化与循环依赖检测
6.1 传递依赖限制
默认情况下,OHPM 会安装所有传递依赖(即依赖的依赖)。对于大型项目,这可能导致依赖树深度膨胀。
// oh-package.json5 - 限制传递依赖
{
"dependencies": {
"@ohos/net": {
"version": "1.2.0",
"transitive": false // 不安装 net 的传递依赖
}
}
}
使用场景:
- 当某个依赖的传递依赖与项目已有依赖冲突时
- 当只需要依赖的核心功能,不需要其附属库时
- 当传递依赖体积过大且功能非必需时
6.2 循环依赖检测
循环依赖(A → B → C → A)会导致编译时依赖解析死循环或运行时初始化异常。
# 使用 hvigor 的 --analyze 选项检测循环依赖
hvigorw assembleRelease --analyze
# 循环依赖报错示例:
# ERROR: Circular dependency detected:
# module_a -> module_b -> module_c -> module_a
#
# Solution: Extract common code into a new HSP module.
循环依赖的解决方案:
- 提取公共代码:将循环依赖中的公共部分提取为独立的 HSP 模块
- 接口隔离:使用接口(Interface)解耦模块间的直接依赖
- 事件总线:使用事件机制替代直接的模块调用
// 解耦前(循环依赖)
// module_a/ets/A.ets
import { B } from '@myapp/module_b'; // A → B
// module_b/ets/B.ets
import { C } from '@myapp/module_c'; // B → C
// module_c/ets/C.ets
import { A } from '@myapp/module_a'; // C → A (循环!)
// 解耦后(事件总线)
// shared_events/ets/EventBus.ets
export class EventBus {
private static listeners: Map<string, Array<(data: any) => void>> = new Map();
static on(event: string, callback: (data: any) => void): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(callback);
}
static emit(event: string, data: any): void {
this.listeners.get(event)?.forEach(cb => cb(data));
}
}
// module_a/ets/A.ets
import { EventBus } from '@myapp/shared_events';
EventBus.emit('module_a_ready', { status: 'ok' });
// module_c/ets/C.ets
import { EventBus } from '@myapp/shared_events';
EventBus.on('module_a_ready', (data) => {
console.log('Module A is ready:', data);
});
七、Feature HAP 按需加载:终极依赖优化
对于非核心功能模块(如客服聊天、地图导航、支付功能),可以拆分为独立的 Feature HAP,通过动态导入按需加载。
// 动态导入 Feature 模块
async function openCustomerService() {
try {
// 首次调用时下载并加载 Feature HAP
const module = await import('@myapp/feature_customer_service');
module.launchCustomerService();
} catch (err) {
console.error('模块加载失败:', err);
promptAction.showToast({ message: '功能加载失败,请检查网络' });
}
}
// 工程级 build-profile.json5 配置
{
"modules": [
{ "name": "entry", "srcPath": "./entry" },
{
"name": "feature_customer_service",
"srcPath": "./feature_customer_service",
"targets": [{ "name": "default", "applyToProducts": ["default"] }]
},
{
"name": "feature_map",
"srcPath": "./feature_map",
"targets": [{ "name": "default", "applyToProducts": ["default"] }]
}
]
}
效果:
- 初始安装包仅包含 Entry HAP 和必要的 HSP
- Feature HAP 在用户首次触发功能时下载
- 典型场景下初始包体积减少 60-70%
八、实战案例:中型应用依赖库精简
8.1 项目概况
- 模块数量:Entry HAP ×1 + Feature HAP ×3 + HAR ×8
- 三方依赖:15 个
- 初始依赖体积:28.5 MB
- 初始总包体积:156.3 MB
8.2 优化步骤与效果

| 优化项 | 优化前 | 优化后 | 减少比例 | 具体措施 |
|---|---|---|---|---|
| overrides 版本统一 | 依赖 28.5MB | 依赖 20.0MB | 30% | 统一 5 个冲突库版本 |
| HAR→HSP 替换 | 重复 12MB | 重复 0MB | 100% | 3 个 HAR 改为 HSP |
| ohpm prune | 未使用 8MB | 未使用 0MB | 100% | 清理 4 个未使用依赖 |
| deduplicateHar | 重复 HAR 6MB | 重复 HAR 3MB | 50% | DevEco 6.0+ 自动去重 |
| 传递依赖限制 | 传递依赖 15MB | 传递依赖 9MB | 40% | 限制 3 个库的传递依赖 |
| 过期依赖升级 | 旧版本 5MB | 新版本 4MB | 20% | 升级 2 个过期库 |
| Feature HAP 拆分 | 初始包 156MB | 初始包 48MB | 70% | 2 个功能模块按需加载 |
| 循环依赖修复 | 编译不稳定 | 编译稳定 | — | 提取公共 HSP 解耦 |
| 合计 | 156.3MB | 48.0MB | 69% | — |
8.3 优化配置汇总
// 工程级 oh-package.json5
{
"name": "MyHarmonyApp",
"version": "2.0.0",
"overrides": {
"@ohos/aki": "1.1.0",
"@ohos/net": "1.2.0",
"@ohos/crypto": "2.0.1",
"@ohos/file.picker": "1.0.5",
"@ohos/chart": "3.0.0"
},
"resolutionStrictness": "strict"
}
// 工程级 build-profile.json5
{
"apiType": "stageMode",
"buildOption": {
"packOptions": {
"deduplicateHar": true
}
},
"useNormalizedOHMUrl": true,
"modules": [
{ "name": "entry", "srcPath": "./entry" },
{ "name": "shared_utils", "srcPath": "./shared_utils" },
{ "name": "shared_network", "srcPath": "./shared_network" },
{ "name": "feature_pay", "srcPath": "./feature_pay" },
{ "name": "feature_chat", "srcPath": "./feature_chat" }
]
}
九、CI/CD 集成:自动化依赖治理
# .github/workflows/dependency-check.yml
name: Dependency Governance
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check dependency conflicts
run: |
ohpm list --depth=3 --json > dependency-tree.json
conflicts=$(cat dependency-tree.json | jq '[.. | objects | select(has("version")) | {name: keys[0], version: .version}] | group_by(.name) | map(select(length > 1))')
if [ "$conflicts" != "[]" ]; then
echo "❌ 发现依赖版本冲突:"
echo "$conflicts" | jq '.'
exit 1
fi
echo "✅ 依赖冲突检查通过"
- name: Check outdated dependencies
run: |
ohpm outdated --json > outdated.json
outdated_count=$(cat outdated.json | jq 'length')
if [ "$outdated_count" -gt 5 ]; then
echo "⚠️ 发现 $outdated_count 个过期依赖,建议升级"
cat outdated.json | jq '.[] | {name, current, latest}'
fi
- name: Check for unused dependencies
run: |
ohpm prune --all --dry-run
- name: Build with analyze
run: |
hvigorw assembleRelease --analyze
- name: Check bundle size
run: |
size=$(stat -c%s build/outputs/default/packaging/app-signed.app)
limit=$((50 * 1024 * 1024)) # 50MB
if [ $size -gt $limit ]; then
echo "❌ 包体积超标: $size bytes > $limit bytes"
exit 1
fi
echo "✅ 包体积检查通过"
- name: Generate dependency report
run: |
echo "## 依赖治理报告" > dependency-report.md
echo "" >> dependency-report.md
echo "### 依赖树概览" >> dependency-report.md
ohpm list --depth=2 >> dependency-report.md
echo "" >> dependency-report.md
echo "### 过期依赖" >> dependency-report.md
ohpm outdated >> dependency-report.md || true
十、总结与最佳实践
本文从钻石依赖治理出发,系统讲解了 HarmonyOS 依赖库精简的全链路方案,涵盖版本统一、重复消除、未使用清理、按需加载等核心技术。
核心最佳实践清单:
- 工程级 overrides:所有多模块工程必须在根目录
oh-package.json5中配置overrides统一关键依赖版本 - HAR→HSP 优先:被多模块引用的共享代码优先使用 HSP,消除重复拷贝
- 定期 prune:每月执行
ohpm prune --all清理未使用依赖 - 过期检测:每季度执行
ohpm outdated检测并升级过期依赖 - 传递限制:对于体积大的依赖,评估是否需要限制其传递依赖
- 循环检测:每次新增模块依赖时,使用
--analyze检测循环依赖 - 按需加载:非核心功能拆分为 Feature HAP,减少初始包体积
依赖库精简不是一次性的"大扫除",而是需要持续监控的日常工程实践。通过建立规范化的依赖治理体系和自动化的检查机制,可以确保应用始终保持轻量、稳定、易维护的依赖结构。
转载自:https://blog.csdn.net/u014727709/article/details/164003986
欢迎 👍点赞✍评论⭐收藏,欢迎指正
更多推荐


所有评论(0)