npm vs yarn vs pnpm
npm/yarn install 原理
- 主要分为两个部分, 首先,执行 npm/yarn install 之后,包如何到达项目 node_modules 当中。其次,node_modules 内部如何管理依赖;执行命令后,首先会构建依赖树,然后针对每个节点下的包,会经历下面四个步骤:
- 将依赖包的版本区间解析为某个具体的版本号
- 下载对应版本依赖的 tar 包到本地离线镜像
- 将依赖从离线镜像解压到本地缓存
- 将依赖从缓存拷贝到当前目录的 node_modules 目录
依赖在 node_modules 内部目录结构
javascript
// npm1、npm2 中呈现出的是嵌套结构
node_modules
└─ foo
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
// 设计存在什么问题:
// 1. 依赖层级太深,会导致文件路径过长的问题,尤其在 window 系统下。
// 2. 大量重复的包被安装,文件体积超级大。比如跟 foo 同级目录下有一个baz,两者都依赖于同一个版本的lodash,那么 lodash 会分别在两者的 node_modules 中被安装,也就是重复安装。
// 3. 模块实例不能共享。比如 React 有一些内部变量,在两个不同包引入的 React 不是同一个模块实例,因此无法共享内部变量,导致一些不可预知的 bug。
javascript
// npm3 中呈现出的是嵌套结构
node_modules
├─ foo
| ├─ index.js
| └─ package.json
└─ bar
├─ index.js
└─ package.json
// 1. 依赖结构的不确定性(不确定性是指,foo和bar都依赖 base64的npm,但是版本不一致,谁先被提到node_modules下面的是不确定的)。
// 2. 扁平化算法本身的复杂性很高,耗时较长。
// 3. 项目中仍然可以非法访问没有声明过依赖的包
- pnpm 结构
- 根目录下的 node_modules 下面不再是眼花缭乱的依赖,而是跟 package.json 声明的依赖基本保持一致。即使 pnpm 内部会有一些包会设置依赖提升,会被提升到根目录 node_modules 当中,但整体上,根目录的 node_modules 比以前还是清晰和规范了许多
- 外层的目录会映射到 pnpm 目录下的包里面
总结
npm(包管理鼻祖)
- 优点
- 官方支持:npm 是 Node.js 的官方包管理器,因此它得到了广泛的支持和使用。
- 自动依赖项解析:npm 可以自动解析项目中的依赖项,并安装所需的软件包。
- 版本控制:npm 使用 package-lock.json 文件来确保安装过程中使用相同的依赖项版本。
- 缺点
- 安装速度较慢:由于 npm 是单线程解析依赖项,因此安装速度可能相对较慢。
- 文件冲突:由于 npm 使用 package-lock.json 文件来锁定依赖项版本,
yarn(解决 npm 不能并发和锁版本问题)
- 优点
- 安装速度快:由于 yarn 可以并行下载和缓存软件包,因此安装速度通常比 npm 快。
- 版本控制:yarn 使用 yarn.lock 文件来确保安装过程中使用相同的依赖项版本。
- 离线模式:yarn 支持离线模式,可以在没有互联网连接的情况下工作。https://www.yarnpkg.cn/features/offline-cache
缺点
- 依赖项缓存:yarn 将所有软件包都缓存在本地,这可能会占用大量磁盘空间。
pnpm(解决幽灵依赖问题)
- 优点
- 安装速度最快:由于 pnpm 可以共享依赖项,因此它可以更快地安装和更新模块。
- 多线程解析:与 yarn 类似,pnpm 也可以使用多线程解析依赖项。
- 离线模式:pnpm 支持离线模式,并且可以在没有互联网连接的情况下工作。
- 更好的内存管理:pnpm 使用更好的内存管理技术,可以更有效地利用系统资源。
- 缺点
- 相对较新:由于 pnpm 是较新的包管理器,因此可能会缺乏一些 npm 和 yarn 中的功能和资源。
- 可能存在兼容性问题:由于 pnpm 采用了不同的依赖项解析方法,因此可能会存在一些兼容性问题。