HarmonyOS 7 API 26 oh-package 依赖锁定图

HarmonyOS 工程里,依赖问题经常不是代码写错,而是版本漂移。本地刚装完能跑,换一台机器、换一次 CI、或者清掉缓存后突然构建失败。这个时候如果只改业务代码,很可能越改越乱。

这篇按 HarmonyOS 7 / API 26 工程来讲 oh-package 依赖锁定。重点是三件事:依赖版本怎么写,lock 文件怎么守住,CI 里怎么提前发现版本漂移。

版本环境先写清楚

项目 示例口径 说明
HarmonyOS 目标 HarmonyOS 7 / API 26 当前文章讨论的新工程目标
工程依赖 oh-package.json5 记录直接依赖
锁定文件 oh-package-lock.json5 记录解析后的确定版本
CI 检查 安装、构建、依赖差异检查 防止本地和流水线不一致

这几项不说清楚,后面讨论依赖问题很容易变成“我这里可以”。工程协作里,“我这里可以”不是结论,CI 可复现才是结论。

直接依赖和锁定依赖不是一回事

oh-package.json5 里写的是工程想要什么,lock 文件里记录的是最终装到了什么。两者都重要。

~~~json

{

"dependencies": {

"@ohos/example-ui": "1.2.3",

"@ohos/example-utils": "^2.0.0"

},

"devDependencies": {

"@ohos/linter-rules": "0.8.0"

}

}

~~~

如果依赖写成 ^2.0.0,后面可能解析到 2.0.1、2.1.0。功能看似没变,但构建输出、类型定义、运行行为都可能变化。多人协作和 CI 里,我更倾向把核心依赖锁得更具体。

案例一:本地能跑,CI 构建失败

问题场景很典型:开发机已经有缓存,所以构建通过;CI 是干净环境,重新安装依赖后失败。

先写一个依赖检查脚本,把直接依赖和 lock 文件都纳入检查。

~~~ts

type DependencyMap = Record<string, string>

type DependencyCheckResult = {

name: string

declared: string

locked?: string

ok: boolean

reason?: string

}

function checkLockedDependencies(declared: DependencyMap, locked: DependencyMap): DependencyCheckResult[] {

return Object.keys(declared).map(name => {

const declaredVersion = declared[name]

const lockedVersion = locked[name]

if (!lockedVersion) {

return { name, declared: declaredVersion, ok: false, reason: 'missing in lock file' }

}

if (declaredVersion !== lockedVersion && !declaredVersion.startsWith('^')) {

return { name, declared: declaredVersion, locked: lockedVersion, ok: false, reason: 'version mismatch' }

}

return { name, declared: declaredVersion, locked: lockedVersion, ok: true }

})

}

~~~

这段代码不依赖具体包管理器输出,先把检查逻辑跑清楚。真正接入 CI 时,可以从 oh-package.json5 和 lock 文件里读取数据。

CI 里不要只跑构建

只跑 build 太晚了。依赖漂移应该在构建前就暴露。

~~~ts

function assertDependencyResult(results: DependencyCheckResult[]): void {

const failed = results.filter(item => !item.ok)

if (failed.length === 0) {

console.info('[dependency-check] passed')

return

}

for (const item of failed) {

console.error(

'[dependency-check] failed',

item.name,

'declared=' + item.declared,

'locked=' + (item.locked ?? 'none'),

item.reason ?? ''

)

}

throw new Error('dependency check failed')

}

~~~

CI 的目标不是把错误藏起来,而是让错误尽早、尽准地失败。依赖不一致就应该在依赖检查阶段失败,不要等到编译阶段出现一堆无关报错。

案例二:三方库升级后类型变化

第二类问题是依赖升级后 API 没报明显错误,但类型定义变了。比如之前返回 string,升级后可能返回 string | undefined。

~~~ts

type OldApiResult = {

title: string

}

type NewApiResult = {

title?: string

}

function normalizeTitle(result: NewApiResult): string {

return result.title?.trim() || '未命名内容'

}

~~~

依赖升级后,不能只看构建是否通过,还要看关键调用是否有兼容层。对业务入口多的项目,我会把三方库调用包一层 adapter。

~~~ts

class ThirdPartyAdapter {

parseTitle(result: NewApiResult): string {

return normalizeTitle(result)

}

assertRuntimeCompatible(version: string): void {

if (!version.startsWith('2.')) {

throw new Error('unsupported dependency version: ' + version)

}

}

}

~~~

这样后面库再升级,影响点集中在 adapter,不会散落到页面里。

本地验证脚本

先用假数据验证依赖检查能不能挡住问题。

~~~ts

function verifyDependencyCheck(): void {

const declared = {

'@ohos/example-ui': '1.2.3',

'@ohos/example-utils': '^2.0.0'

}

const locked = {

'@ohos/example-ui': '1.2.4',

'@ohos/example-utils': '2.1.0'

}

const result = checkLockedDependencies(declared, locked)

console.info('[verify-deps]', JSON.stringify(result))

}

~~~

预期结果是 example-ui 被拦住,因为声明 1.2.3,实际锁定 1.2.4;example-utils 因为声明了 ^2.0.0,需要看团队规则是否允许浮动。如果团队追求完全可复现,也可以把 ^ 依赖一起拦掉。

我会采用的规则

规则 原因
核心运行依赖写精确版本 减少线上行为变化
lock 文件必须提交 保证团队和 CI 一致
CI 构建前先查依赖 让版本问题提前失败
三方库调用包 adapter 升级影响集中处理
升级依赖要有回归清单 避免只看能不能编译

回归清单

检查项 通过标准
清缓存安装 干净环境能安装成功
依赖锁定 lock 文件和声明依赖一致
CI 构建 构建失败能定位到依赖阶段
类型变化 adapter 层能处理 undefined 等变化
关键页面 升级后核心页面可打开、可返回、可保存

小结

HarmonyOS 7 / API 26 工程里,oh-package 依赖管理不能只靠本地缓存。直接依赖、lock 文件、CI 检查和 adapter 兼容层要一起看。这样遇到“本地能跑、流水线失败”时,先查依赖锁定,而不是一上来怀疑业务代码。

Logo

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

更多推荐