包管理器
背景
如今在市面上,主流的javascript包管理器主要为npm,yarn,pnpm,但这些又存在着什么样的差异性呢,让我们接下去往下看:
介绍
npm (Node Package Manager)
- Node.js 的官方包管理器
- 随 Node.js 一起安装
Yarn
- 最初是为了解决 npm 的某些性能和安全问题,拥有更快的下载速度(并行下载)
- 现在有两个主要版本: Yarn 1 (经典版)和 Yarn 2+ (Berry)
pnpm(需要Node 16+
- 使用硬链接和符号链接来节省磁盘空间
- 旨在解决 npm 和 yarn 的依赖重复问题(解决幽灵依赖)
版本控制符
在项目中package.json
中每个依赖项总是存在一些特定的符号,如~
、^
、*
前缀/格式 | 安装行为示例 |
---|---|
1.2.3 | 1.2.3 (严格匹配) |
~1.2.3 | 1.2.3 → 1.2.9 (仅更新补丁) |
^1.2.3 | 1.2.3 → 1.9.0 (更新次版本+补丁) |
^0.2.3 | 0.2.3 → 0.2.9 (0.x 表现同 ~) |
1.x | 1.0.0 → 1.999.999 |
幽灵依赖
幽灵依赖是指在项目中没有声明在package.json
中的包,但确能直接被使用;这些没声明但能被使用的包实际上项目依赖的间接依赖(即是依赖中的依赖)。因此,产生的核心原因是扁平化 node_modules 结构直接相关
具体实例
your-project
└── depends-on-A@1.0.0
└──Lodash@4.17.0
在 npm/yarn 的扁平化结构下:
node_modules/
├── depends-on-A/ # 你的直接依赖
└── lodash/ # 被提升的间接依赖(幽灵依赖!)
虽然你的 package.json 中没有声明 lodash,但代码中可以这样使用:
const _ = require('lodash'); // 可以工作,但这是幽灵依赖!
pnpm相关处理
pnpm 使用不同的 node_modules 结构:
node_modules/
├── .pnpm/ # 所有依赖的实际存储位置
├── depends-on-A/ # 只包含 depends-on-A 的代码
└── lodash/ # 不存在! 因为未被显式声明
在 pnpm 中:
- 每个包只能访问自己 package.json 中声明的依赖
- 尝试 require 未声明的依赖会立即失败
- 通过符号链接实现严格的依赖隔离
Peer Dependencies
Peer Dependencies(对等依赖)是 package.json 中的一种特殊依赖类型,用于声明 "我的包需要宿主环境提供这些依赖,但我不想自己安装"
如下场景:
webpack-loader需要宿主项目安装
webpack,那么需要package.json声明
{
"peerDependencies": {
"webpack": "^5.0.0"
}
}
总结
特性 | npm | yarn | pnpm |
---|---|---|---|
安装速度 | 中等 | 快(并行安装) | 最快(利用硬链接) |
磁盘空间使用 | 高(重复依赖) | 高(重复依赖) | 低(共享依赖) |
锁定文件 | package-lock.json | yarn.lock | pnpm-lock.yaml |
依赖结构 | 扁平化(node_modules) | 扁平化(node_modules) | 嵌套+符号链接(.pnpm store) |
离线模式 | 支持(npm ci) | 支持(yarn --offline) | 默认支持 |
workspaces | 支持 | 支持 | 支持(优化更好) |
CLI 体验 | 基础 | 丰富 | 类似npm |
全局存储 | 无 | 无 | 有(.pnpm-store) |