NPM 包引用踩坑
NPM 包引用踩坑
问题
已知在项目开发过程中, 项目 P 和依赖的模块 M 都用到了另一个模块 M2.
有以下几种情况:
- M2 只安装在项目 P(模块提升): 在项目 P 和模块 M 中引用的模块 M2 是同一个文件, 获得的是同一个对象(单例)
- M2 安装在项目 P 中和模块 M 中: 这种情况下在项目 P 中和模块 M 其实引用到了不同的文件, 获得的是不同的对象(非单例)
在文章后提供了相关源代码, 两个项目中的
package.json 都是一样的, 不一样的只是 package-lock.json复现
已知
@fe/framework 依赖最新的 @fe/config 项目- 开一个新的项目, 安装
@fe/framework, 此时项目的node_modules里 @fe 下面应该有两个包, 分别是config和framework
- 安装
@fe/config的降级版本, 此时会在项目的 node_module 下安装@fe/config的降级包; 因为@fe/framework的包仍旧依赖最新版本, 所以会在它的里面也会生成node_modules, 并且会安装@fe/config的最新包; 此时在项目和@fe/framework中引用到的config包是不一样的, 所以不是同一个单例, 配置会出错
- 就算现在更新项目中的
@fe/config到最新版本(即和@fe/framework中保持一致), 仍旧是引用的不同的单例, 也会出现错误
- 此时删除项目中的
@fe/config依赖并重新安装, 只是删除项目中的@fe/config而已, 所以会导致项目中找不到config包
会出错的代码如下:
解决方法
解决方法 1
- 确保项目中依赖的
@fe/config版本要满足@fe/framework中依赖的@fe/config版本, 如果可以的话, 删除项目中对@fe/config的依赖
- 删除
@fe/framework, 并且重装@fe/framework即可
- 此时如果执行
npm ls @fe/config命令, 仍应该会显示有两个 @fe/config 包, 但是@fe/framework下的@fe/config应该会显示deduped, 代表已经被去重删除了
结果如下:
➜ test npm ls @fe/config test@1.0.0 /Users/jiyu/Code/personal/test ├── @fe/config@2.5.3 └─┬ @fe/framework@5.3.0-rc.0 └── @fe/config@2.5.3 deduped解决方法 2
- 确保项目中依赖并安装的
@fe/config版本要满足@fe/framework中依赖的@fe/config版本
- 执行
npm ddp, 来进行去重并上提通用包
结果如下:
➜ test npm ls @fe/config test@1.0.0 /Users/jiyu/Code/personal/test ├── @fe/config@2.5.3 └─┬ @fe/framework@5.3.0-rc.0 └── @fe/config@2.5.3 deduped建议
锁定
package.json 版本, 然后部署的时候使用 npm ci 来进行安装, 这样的话比较容易统一开发环境和构建环境的 node 环境结论
- 在第一次安装的情况下,
npm会提升并合并packages的dependency
- 但是之后手动安装或者更新了
package.json之后再安装的情况下, 如果同一个版本的dependency不能满足多个引用到它的包的情况下, 还是会分开安装
- 导致不同的包可能引用到不同的
dependency, 会导致版本不一致的问题, 并且会生成不同的单例
- 就算此时撤销更改, 虽然
package.json里看起来和一开始一样, 但是已经有多个dependency分散在不同的包下
- 此时就算版本一致, 但是还是引用的不同的包, 所以会产生多个单例
- 建议使用
package-lock.json来保留安装信息
参考
- Nodejs 模块加载
- Are Node.js modules singletons?