文章目录
- 每日一句正能量
- 一、前言:依赖库是应用体积的"沉默膨胀器"
- 二、HarmonyOS 依赖库精简技术全景图
- 三、钻石依赖问题与 overrides 版本统一
- 3.1 钻石依赖问题的本质
- 3.2 overrides:强制统一依赖版本
- 3.3 本地包替换临时修复
- 四、HAR → HSP:消除重复代码拷贝
- 4.1 HAR 与 HSP 的本质差异
- 4.2 HAR → HSP 替换实战
- 4.3 DevEco Studio 6.0+ 自动去重
- 五、ohpm 依赖分析工具链
- 5.1 ohpm list:查看完整依赖树
- 5.2 ohpm prune:清理未使用依赖
- 5.3 ohpm outdated:检测过期依赖
- 5.4 --analyze:编译性能分析
- 5.5 app-check-tool:重复依赖扫描
- 六、传递依赖优化与循环依赖检测
- 6.1 传递依赖限制
- 6.2 循环依赖检测
- 七、Feature HAP 按需加载:终极依赖优化
- 八、实战案例:中型应用依赖库精简
- 8.1 项目概况
- 8.2 优化步骤与效果
- 8.3 优化配置汇总
- 九、CI/CD 集成:自动化依赖治理
- 十、总结与最佳实践
每日一句正能量
求木之长者,必固其根本;欲流之远者,必浚其泉源。
无论是个人成长、事业建设还是关系经营,追逐枝叶的繁茂(速成、表象)不如深耕根本(能力、品德、健康);想要源远流长,就必须疏通源头(初心、动机、系统)。一切长久的美好,都离不开扎实的基础。
一、前言:依赖库是应用体积的"沉默膨胀器"
在 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 StorageManager4.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--duplicates5.2 ohpm prune:清理未使用依赖
# 清理当前模块中未使用的依赖ohpm prune# 清理所有模块的未使用依赖ohpm prune--all# 清理并更新 oh-package-lock.json5ohpm 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--security5.4 --analyze:编译性能分析
# 生成编译性能依赖图hvigorw assembleRelease--analyze# 分析结果保存在 build/reports/analyze/# 包含:# - 各模块编译耗时# - 依赖解析耗时# - 循环依赖检测# - 冗余依赖警告5.5 app-check-tool:重复依赖扫描
# 扫描 HAP/HSP 包中的重复 HARjava-jar$OHOS_SDK/toolchains/lib/app-check-tool.jar\--modehap\--inputbuild/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.etsimport{B}from'@myapp/module_b';// A → B// module_b/ets/B.etsimport{C}from'@myapp/module_c';// B → C// module_c/ets/C.etsimport{A}from'@myapp/module_a';// C → A (循环!)// 解耦后(事件总线)// shared_events/ets/EventBus.etsexportclassEventBus{privatestaticlisteners:Map<string,Array<(data:any)=>void>>=newMap();staticon(event:string,callback:(data:any)=>void):void{if(!this.listeners.has(event)){this.listeners.set(event,[]);}this.listeners.get(event)!.push(callback);}staticemit(event:string,data:any):void{this.listeners.get(event)?.forEach(cb=>cb(data));}}// module_a/ets/A.etsimport{EventBus}from'@myapp/shared_events';EventBus.emit('module_a_ready',{status:'ok'});// module_c/ets/C.etsimport{EventBus}from'@myapp/shared_events';EventBus.on('module_a_ready',(data)=>{console.log('Module A is ready:',data);});七、Feature HAP 按需加载:终极依赖优化
对于非核心功能模块(如客服聊天、地图导航、支付功能),可以拆分为独立的 Feature HAP,通过动态导入按需加载。
// 动态导入 Feature 模块asyncfunctionopenCustomerService(){try{// 首次调用时下载并加载 Feature HAPconstmodule=awaitimport('@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.ymlname:Dependency Governanceon:[push,pull_request]jobs:check:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:Check dependency conflictsrun:|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 dependenciesrun:|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 dependenciesrun:|ohpm prune --all --dry-run-name:Build with analyzerun:|hvigorw assembleRelease --analyze-name:Check bundle sizerun:|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 reportrun:|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
欢迎 👍点赞✍评论⭐收藏,欢迎指正