ohpm 入门:正确区分生产依赖、开发依赖与动态依赖
专栏:HarmonyOS 三方库适配实战(3/30)
本篇目标:让依赖出现在正确的生命周期,而不是“哪里能装就写在哪里”。
摘要
HarmonyOS 工程的oh-package.json5可以声明dependencies、devDependencies和dynamicDependencies。三者不是三个随意选择的收纳盒:生产依赖会参与编译和运行,开发依赖只服务开发或测试,动态依赖用于配合动态导入表达 HSP 依赖。本文通过一个本地 HAR 示例说明应该把依赖放在哪里、如何安装、怎样确认最终依赖树,并分析把运行期库误放进开发依赖后的故障。
适用范围与完成标志
本文讨论模块或工程中的 ohpm 依赖声明,不讨论 HSP 的完整部署流程。你需要能找到目标模块的oh-package.json5,并知道哪个公开 API 会在正式代码中被调用。
完成本篇后,不只是“知道三个字段的中文名”,还应能给每个直接依赖写出使用阶段、消费模块、实际导入点和验证命令。无法说明用途的依赖,不应仅凭安装成功就保留。
先记住一个判断问题
不要先问“这个库叫什么”,先问:
最终应用在目标设备执行公开功能时,是否仍然需要这个依赖?
- 如果需要,通常属于
dependencies。 - 如果只在开发、测试或构建辅助阶段使用,属于
devDependencies。 - 如果它是通过动态导入按条件加载的 HSP,才考虑
dynamicDependencies。
图 1:依赖分类判断流程(示意图)。判断依据是发布产物中的运行责任。
华为官方oh-package.json5文档明确说明:生产依赖参与编译/运行;开发依赖只能参与开发或测试;动态依赖用于动态加载 HSP。三者的语义由交付方式和运行路径决定,不由包体积或使用频率决定。
示例:给应用接入一个本地 HAR
为了不假装某个远程示例包永远存在,本篇使用本地 HAR 路径。假设适配库包名为@demo/text-tool,产物放在模块的libs/text-tool.har。它是教学包名,不代表公共仓库中存在同名包。
在消费模块的oh-package.json5中配置:
{ "name": "entry", "version": "1.0.0", "main": "Index.ets", "dependencies": { "@demo/text-tool": "file:./libs/text-tool.har" }, "devDependencies": {} }然后在包含该oh-package.json5的正确目录安装依赖:
ohpminstall--allohpm list-d10官方安装 FAQ 特别提醒:安装成功却看不到依赖时,要先检查执行命令的目录。工程级和模块级都可能存在oh-package.json5,安装产物与修改的配置文件必须对应。排查时同时记录命令所在目录、配置文件位置和ohpm --version。
图 2:本地复现实验记录。HAR 被ohpm解析并参与 HAP 构建;设备调用仍待验证。
给 HAR 准备一个可观察 API
假设@demo/text-tool的公开入口导出下面函数。文件位置应由库工程的入口配置决定,例如Index.ets:
exportfunctionnormalizeSpaces(input:string):string{returninput.trim().replace(/\s+/g,' ')}消费方通过包名导入,而不是跨目录引用库内部源码:
import{normalizeSpaces}from'@demo/text-tool'constinput:string=' HarmonyOS third-party library 'constoutput:string=normalizeSpaces(input)console.info(`normalized=${output}`)若该库的公开契约就是压缩连续空白,预期输出可以写为:
normalized=HarmonyOS third-party library这里的输出是根据示例函数推导的预期值,不是本轮设备日志。真实文章发布前,应使用目标 SDK 编译并在模拟器或真机读取实际日志。
dependencies:运行时仍需要的生产依赖
下面情况通常属于生产依赖:
- 应用或 HAR 的公开源码中直接
import的工具库; - 最终功能调用链上的组件、资源或 Native 库;
- 消费方构建和运行都必须解析的依赖。
一个 HAR 如果公开 API 内部依赖另一个包,也要根据实际交付关系正确声明,不能指望消费方碰巧安装了相同依赖。
devDependencies:只服务开发与测试
典型例子包括:
- 测试框架;
- 只在本地生成代码或检查格式的工具;
- 不进入最终运行调用链的开发辅助包。
不要因为“这个包只有调试时才容易出错”就放进开发依赖。判断标准仍然是最终产物运行时是否需要它。
下面是教学坐标,不代表公共仓库中存在该包;真正使用时要换成项目已经核对的测试工具及准确版本:
{ "devDependencies": { "@demo/test-runner": "1.0.0" } }dynamicDependencies:动态导入的 HSP 依赖
动态依赖不是“偶尔使用的普通库”。官方添加依赖项文档说明,dynamicDependencies仅限动态依赖 HSP 的场景。只有工程确实采用动态加载设计,并且目标模块、产品和部署关系都满足要求时才使用。
概念配置如下:
{ "dynamicDependencies": { "feature_chat": "file:./libs/feature_chat.hsp" } }在没有建立 HSP、动态加载和目标产品验证之前,不要为了“提升启动速度”把普通 HAR 随意移到这里。
故障复盘:把运行库放入devDependencies
假设entry在正式代码中导入@demo/text-tool,但配置写成:
{ "devDependencies": { "@demo/text-tool": "file:./libs/text-tool.har" } }开发阶段可能因为缓存、其他模块或本地环境而暂时看不出问题,但它的声明已经与实际用途矛盾。排查步骤是:
- 从报错导入反查实际调用链;
- 查看当前模块的
oh-package.json5; - 使用
ohpm list -d 10检查最终依赖位置; - 将真正的生产依赖移到
dependencies; - 重新安装,并在干净环境构建消费模块;
- 记录锁文件和构建结果的变化。
不要只通过复制oh_modules目录来掩盖错误声明。那会让本机暂时通过,却让 CI 或新电脑失败。
用干净环境确认修复
调整依赖分类后,至少比较以下证据:
| 检查 | 调整前 | 调整后 |
|---|---|---|
oh-package.json5所在模块 | 记录 | 记录 |
ohpm list -d 10中的节点 | 保存 | 保存 |
| 锁文件是否变化 | 是/否 | 是/否 |
| 目标模块构建 | 失败/未知 | 实际退出状态 |
| 核心调用 | 未执行/失败 | 实际运行结果 |
不要删除锁文件和安装目录来制造“干净”。如果确需清理,先保存现场,再依据当前 ohpm 文档选择保留锁文件的方式。
一张可复用的依赖分类表
| 问题 | 是 | 否 |
|---|---|---|
| 正式源码是否直接或间接调用它? | 倾向dependencies | 继续判断 |
| 设备运行时是否必须携带它? | dependencies | 继续判断 |
| 是否只用于测试、检查或开发辅助? | devDependencies | 继续判断 |
| 是否通过动态 import 加载 HSP? | dynamicDependencies | 不要使用动态依赖 |
本篇产物
- 一个本地 HAR 的模块级依赖配置。
- 一段公开入口与消费方调用代码。
- 一套生产、开发和动态依赖的判断方法。
进入下一篇前,应该能运行ohpm list并指出每一个直接依赖为何出现在当前分类中。
官方来源
- oh-package.json5:用于三类依赖的定义、远程/本地 HAR/源码等依赖方式。访问日期:2026-10-15。
- 添加依赖项:用于确认
dynamicDependencies仅限动态依赖 HSP 的场景。访问日期:2026-10-15。 - 如何配置 oh-package.json5 动态依赖:用于生产、开发、动态依赖的当前说明。访问日期:2026-10-15。
- ohpm list:用于查看直接和间接依赖。访问日期:2026-10-15。
- ohpm 安装常见问题:用于命令执行目录与安装产物的排查。访问日期:2026-10-15。
验证声明
本文已于 2026-10-15 核对华为官方oh-package.json5、添加依赖项、动态依赖、ohpm list和安装 FAQ。实验工程已生成@demo/text-tool@1.0.0HAR,将其作为 entry 的dependencies本地文件依赖安装;ohpm list -d 10能读回该节点,安装与列表命令退出码均为 0,消费代码通过 API 22 目标 SDK 编译。@demo/*仍是本地教学坐标,不是公共仓库包;本轮没有实际 HSP、模拟器或真机验证。