npm、yarn、pnpm 是常见的包管理工具
他们都是将一个一个的包下载并安装在项目的 node_modules 中
1. npm
首先是 npm,它是 node.js 自带的包管理工具,但是在现代项目中并不常用,主要原因就是
- 安装速度慢
node_modules占用空间大
1.1 npm install 原理
npm 会首先处理项目根目录下的依赖,然后逐层处理每个依赖包的依赖,直到所有依赖都被处理完毕(广度优先算法)。在处理每个依赖时,npm 会检查该依赖的版本号是否符合依赖树中其他依赖的版本要求,如果不符合,则会尝试安装适合的版本
最终形成的目录结构应该是这样的
比如安装 express
1.2 npm install 流程
1.3 npm2.x 和树形依赖目录
在早期 npm2.x 时代,npm 还没有支持扁平化安装,因此得到的安装目录就如上图所示
1.4 npm3.x 和扁平化依赖目录
npm3.x 引入了扁平化安装的方式,将所有的依赖都放到根目录来了
这会导致「幻影依赖/幽灵依赖」的问题,是指某个包未在 package.json 中定义,但项目中依然可以引用到的情况
但是因为 npm install 时,遍历 dependencies 是根据依赖的顺序生成的,如果顺序改变,就会导致生成的目录结构不一致
1.5 npm5.x 和依赖锁定
npm5.x 引入了 package-lock.json 和缓存机制(当之前安装过这个包,就会从缓存目录中直接复制到 node_modules 中)
package-lock.json
只有 package.json 时,npm 无法保证每次安装都能生成一样的 node_modules 树,也就可能导致以下问题:
- 多人协作时,依赖不一样导致开发不一致
- 发布上线时,服务器运行
npm install生成了和在本地开发时不一样的依赖树,就可能导致线上问题 原因主要有 - 比如
package.json中声明的依赖:A: '^1.0.5',因为是^,所以当A升级之后,就会安装新的依赖,导致依赖树不一致(依赖升级) - 还是依赖
A,如果它依赖的B,升级了,也会导致依赖书不一致,(依赖的依赖升级)
为了解决依赖不一致的问题,npm5.x 在 install 之后会生成一个 package-lock.json 文件
npm install执行时,如果package.json和package-lock.json中的版本兼容,会根据package-lock.json中的版本下载;如果不兼容,将会根据package.json的版本,更新package-lock.json中的版本,然后再去下载实际包package-lock.json文件锁定所有模块的版本号,包括主模块和所有依赖子模块
package-lock.json 解决了 npm 无法保证每次安装都能生成一样的 node_modules 树的问题
1.6 依赖的区别
dependencies是无论开发环境还是生产环境都会安装的依赖devDependencies是指可以在开发环境使用的依赖,一般指ESLint、Prettier这些。在打包工具中都会排除开发依赖进行打包,可以使用npm install --production仅安装生产依赖optionalDependencies指的是可以选择的依赖,这些依赖下载失败或者没有找到都不会影响npm的安装,但是不要和dependencies中重复peerDependencies用于指定你当前的插件兼容的宿主必须要安装的包的版本,在npm2.x会自动安装,npm3.x之后就只会警告了,需要自行安装
1.7 依赖匹配符号
^插入符号,会匹配到当前major version的最新版本,比如"vue": "^3.4.27"相当于"vue": "3.x.x"~波浪号,会匹配到当前minor version的最新版本,也就是3.4.x
2. yarn
2.1 特点
- yarn 使用多线程的方式安装依赖,而 npm 是单线程
- yarn 可以复用本地缓存的依赖包,相比于 npm 的缓存机制,它支持离线安装
3. pnpm
3.1 pnpm 特点
- 极大节省了硬盘空间
- 生成树形结构目录,而非扁平化目录,防止幽灵依赖
3.2 如何生成 node_modules
- 使用硬连接,将已安装的包链接到
node_modules - 使用符号链接生成
node_modules的嵌套结构(依赖的相互依赖关系使用软连接链接)
参考资料