NPM 包引用踩坑
Last Edited Time
Feb 12, 2022 09:58 AM
date
Dec 31, 2019
slug
npm-package-dependency
status
Published
tags
NodeJS
NPM
Blog
必读系列
个人笔记
summary
NPM 包引用踩坑总结
type
Post
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
包
会出错的代码如下:
export default () => { // 项目引用 config const config = require('@fe/config'); config.load(); console.warn('项目获取配置:', config.get('api')); // @fe/framework 引用 config // 在使用 config.get() 之前必须调用 config.load(), @fe/framework 是不会调用 config.load() // 所以可以根据以下代码是否报错来判断 @fe/config 引用是否为单例 require('@fe/framework/lib/logger'); console.warn('@fe/framework获取配置:', config.get('api')); return true;};
解决方法
解决方法 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
来保留安装信息