在这里插入图片描述

每日一句正能量

求木之长者,必固其根本;欲流之远者,必浚其泉源。
无论是个人成长、事业建设还是关系经营,追逐枝叶的繁茂(速成、表象)不如深耕根本(能力、品德、健康);想要源远流长,就必须疏通源头(初心、动机、系统)。一切长久的美好,都离不开扎实的基础。


一、前言:依赖库是应用体积的"沉默膨胀器"

在 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 的优先级规则

  1. 最高优先级overrides 中的声明
  2. 次优先级:模块级 dependencies 中的声明
  3. 最低优先级:传递依赖的默认版本

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.

循环依赖的解决方案

  1. 提取公共代码:将循环依赖中的公共部分提取为独立的 HSP 模块
  2. 接口隔离:使用接口(Interface)解耦模块间的直接依赖
  3. 事件总线:使用事件机制替代直接的模块调用
// 解耦前(循环依赖)
// 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 依赖库精简的全链路方案,涵盖版本统一、重复消除、未使用清理、按需加载等核心技术。

核心最佳实践清单:

  1. 工程级 overrides:所有多模块工程必须在根目录 oh-package.json5 中配置 overrides 统一关键依赖版本
  2. HAR→HSP 优先:被多模块引用的共享代码优先使用 HSP,消除重复拷贝
  3. 定期 prune:每月执行 ohpm prune --all 清理未使用依赖
  4. 过期检测:每季度执行 ohpm outdated 检测并升级过期依赖
  5. 传递限制:对于体积大的依赖,评估是否需要限制其传递依赖
  6. 循环检测:每次新增模块依赖时,使用 --analyze 检测循环依赖
  7. 按需加载:非核心功能拆分为 Feature HAP,减少初始包体积

依赖库精简不是一次性的"大扫除",而是需要持续监控的日常工程实践。通过建立规范化的依赖治理体系和自动化的检查机制,可以确保应用始终保持轻量、稳定、易维护的依赖结构。


转载自:https://blog.csdn.net/u014727709/article/details/164003986
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

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

更多推荐