Skip to main content

npm多版本包依赖问题探讨

最近在思考这样一个问题,当项目依赖了多个不同版本的包,打包结果和 node_modules 依赖结构会有什么不一样?会不会高版本的覆盖低版本的方法呢?如果覆盖了就可能导致程序 bug 。

为了尝试多版本依赖问题,我写了两个包来测试,已经放在了 npm 上。分别是 @flypkg/main @flypkg/suba

仓库地址是 https://github.com/simonwong/flypkg

验证思路

验证思路是这样的

suba 包暴露了一个 runSubA 方法,会打印出自己当前版本。

export function runSubA () {
logMessage('subA', pkg.version, '在 subA 中执行')
}

main 包依赖与自己相同版本的 suba 并暴露一个 init 放执行 runSubA

export function init () {
logMessage('main', pkg.version, '在 main 中执行')
runSubA()
}

然后在项目中依赖不同版本的 @flypkg/main 和 @flypkg/suba ,并同时执行 initrunSubA 方法

import { init } from '@flypkg/main'
import { runSubA } from '@flypkg/suba'

init()
runSubA()

可能不同版本的 yarn 和 npm 会有不同的结果,这里的测试版本为 yarn@1.22.0 npm@6.14.4

尝试

第一次尝试

我们的 package.json 的版本是这样的。注意:这里的 suba 版本没有加 ^

"dependencies": {
"@flypkg/main": "^0.0.2",
"@flypkg/suba": "0.0.1",
},

执行 yarn list 是这样的

├─ @flypkg/main@0.0.2
│ ├─ @flypkg/suba@^0.0.2
│ └─ @flypkg/suba@0.0.2
├─ @flypkg/suba@0.0.1

输出结果如下

看了下 webpack 的打包结果,存在两个 runSubA 方法,在各自的执行代码块中,对应 0.0.1 和 0.0.2 版本。

第二次尝试

考虑到是否因为 ^ 符号影响,我在 suba 包前面加了 ^ 符号。 ^ 的加入,可以使 install 的时候安装到 0.x.x 的最新版本。

然而实际的依赖树和执行结果同上。

第三次尝试

修改 package.json 如下

"dependencies": {
"@flypkg/main": "^0.0.2",
"@flypkg/suba": "^0.0.3"
},

yarn list 结果如下

├─ @flypkg/main@0.0.2
│ ├─ @flypkg/suba@^0.0.2
│ └─ @flypkg/suba@0.0.2
├─ @flypkg/suba@0.0.3

执行结果如下

依旧是执行不同的依赖版本的方法。

总结

嗯~~~对结果表示心满意足。这样就避免了一开始我所担心的问题。我们可以继续放心的在项目中使用新的依赖了。(≧▽≦)/

等等,我们是不是忽略了什么问题。

问题一

由于存在各种版本的方法,我们最终打包的体积也变的更大了!是的,我们只能眼睁着包的体积变大。

有什么优化手段呢?

就是尽可能的使用 ES Module 规范和支持 tree shaking 的包,就是在包的 package.json 中,写着这样的一行代码 "sideEffects": false

它来告诉 webpack ,如果我这个包有些方法没有被使用,那么你可以尽情的抖掉(不打包)。避免代码冗余。

问题二

如果 main@0.0.3 依赖的 suba@0.0.3 存在着 bug ,我们该怎么解决呢。

使用 resolutions 强行锁版本。yarn 支持,npm 需要使用 npm-froce-resolution 这个库来实现。

/* package.json */
{
"resolutions": {
"@flypkg/suba": "0.0.2"
}
}

相关阅读 node_modules 困境