pnpm 包管理器
pnpm 是一款新兴不久的包管理器,相比于 npm 和 yarn,pnpm 拥有更快的安装速度,同时节约磁盘空间。
介绍
pnpm 是一个类似于 npm 、yarn 的包管理器。
pnpm 安装的包都会被存储在硬盘的某个相同位置,软甲包通过硬链接到这个位置,实现共享同一版本的依赖, 对于同一依赖的不同版本,pnpm update 时,只会向存储中心添加新版本更新的文件,而不是仅仅应为一个文件的改变而复制整个新版本包的内容。
pnpm 内置支持 monorepo,即单仓库多包。
比较
pnpm/yarn/npm
| 功能 | pnpm | yarn | npm |
|---|---|---|---|
| 工作空间支持(monorepo) | ✔️ | ✔️ | ✔️ |
隔离的node_modules | ✔️ - 默认 | ✔️ | ❌ |
提升的node_modules | ✔️ | ✔️ | ✔️ -默认 |
| 自动安装peers | ✔️ - auto-install-peers=true | ❌ | ✔️ |
| Plug'n'Play | ✔️ | ✔️ - 默认 | ❌ |
| 零安装 | ❌ | ✔️ | ❌ |
| 修复依赖项 | ✔️ | ✔️ | ❌ |
| 管理nodejs版本 | ✔️ | ❌ | ❌ |
| 有锁文件 | ✔️ - pnpm-lock.yaml | ✔️ - yarn.lock | ✔️ - package-lock.json |
| 支持覆盖 | ✔️ | ✔️ - resolutions | ✔️ |
| 内容可寻址存储 | ✔️ | ❌ | ❌ |
| 动态包执行 | ✔️ - pnpm dlx | ✔️ - yarn dlx | ✔️ - npx |
| Side-effects cache | ✔️ | ❌ | ❌ |
区别
与 yarn/npm 不同的是,pnpm 并非采用 扁平的node_modules 来管理依赖项, 而是基于符号链接的node_modules 结构。
node_modules 中每个包的每个文件都是来自内容可寻址存储的硬链接。 假设安装了依赖于 [email protected] 的 [email protected]。 pnpm 会将两个包硬链接到 node_modules 如下所示:
node_modules
└── .pnpm
├── [email protected]
│ └── node_modules
│ └── bar -> <store>/bar
│ ├── index.js
│ └── package.json
└── [email protected]
└── node_modules
└── foo -> <store>/foo
├── index.js
└── package.json这是 node_modules 中的唯一的“真实”文件。 一旦所有包都硬链接到 node_modules, 就会创建符号链接来构建嵌套的依赖关系图结构。
bar 将被符号链接到 [email protected]/node_modules 文件夹,然后处理依赖关系,foo 将被符号链接至根目录的 node_modules 文件夹:
node_modules
├── foo -> ./.pnpm/[email protected]/node_modules/foo
└── .pnpm
├── [email protected]
│ └── node_modules
│ └── bar -> <store>/bar
└── [email protected]
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../[email protected]/node_modules/bar这种布局的好处在于,只有真正在依赖项中的包才能访问。 如果是平铺的 node_modules 结构,所有被提升的包都可以访问。
优势
节约磁盘空间
包存储在全局存储中,pnpm 创建从全局存储到项目下
node_modules文件夹的 硬链接,硬链接指向磁盘上原始文件所在的同一位置。不同软件包可以共享相同依赖项所占用的空间。如果是单个依赖的不同版本,如版本更新,
pnpm仅安装版本更新的文件,而不是全量安装整个新版本的包。安装速度快
软件包中安装依赖时,如果检索到在本地的全局存储中已安装过该依赖,那么不会从网络下重新安装,而是直接创建硬链接到软件包中。
内置支持 monorepo
支持 单仓库多包,通过
pnpm-workspace.yaml配置工作空间,通过workspace:*协议引用工作空间的依赖包。
安装
通过 npm 安装
npm install -g pnpm通过 Corepack 安装
从 v16.13 开始,Node.js 发布了 Corepack 来管理包管理器。 这是一项实验性功能,因此需要通过运行如下脚本来启用它:
corpack enabled这将自动在系统上安装 pnpm。 但是,它可能不是最新版本的 pnpm。 若要升级,请检查 最新的 pnpm 版本 并运行:
corepack prepare pnpm@<version> --activate使用独立脚本安装
在 POSIX 系统上,即使没有安装 Node.js,也可以使用以下脚本安装 pnpm:
curl -fsSL https://get.pnpm.io/install.sh | sh -如果没有安装 curl ,也可以使用 wget:
wget -qO- https://get.pnpm.io/install.sh | sh -在 Windows 系统中,如果使用 Powershell:
iwr https://get.pnpm.io/install.ps1 -useb | iex使用 Homebrew 安装
brew install pnpm使用 Scoop 安装
scoop install nodejs-lts pnpm使用
pnpm 在使用上 与 npm 、yarn 的使用上差别不大,但需要注意的区别,pnpm 会严格校验所有参数, 比如,pnpm install --target_arch x64 会执行失败,因为 --target_arch x64 不是 pnpm install 的有效参数。
常用命令
pnpm install
别名 pnpm i
等效于 npm install / yarn
用于安装项目所有依赖。
pnpm add <pkg>
安装软件包及其依赖的任何软件包。 默认情况下,任何新软件包都安装为生产依赖项。
pnpm remove
别名: rm uninstall un
从 node_modules 和项目的 package.json 中删除相关 packages。
pnpm update
别名: up upgrade
pnpm update 根据指定的范围更新软件包的最新版本。
在不带参数的情况下使用时,将更新所有依赖关系。 您可以使用一些模式来更新特定的依赖项。
更多命令请查阅官方文档
配置
.npmrc
pnpm 从命令行、环境变量和 .npmrc 文件中获取其配置。
pnpm config 命令可用于更新和编辑 用户和全局 .npmrc 文件的内容。
四个相关文件分别为:
- 每个项目的配置文件(
/path/to/my/project/.npmrc) - 每个工作区的配置文件(包含
pnpm-workspace.yaml文件的目录) - 每位用户的配置文件(
~/.npmrc) - 全局配置文件(
/etc/.npmrc)
pnpm-workspace.yaml
pnpm-workspace.yaml 定义了 工作空间 的根目录,并能够使工作空间中包含 / 排除目录 。 默认情况下,包含所有子目录。
packages:
# 定义 packages 目录下的所有子目录都为一个 package
- 'packages/*'
# 定义 components 目录下的所有子目录都为一个 package
- 'components/**'
# 排除任何目录中的 test 目录下的所有目录
- '!**/test/**'工作空间
pnpm 内置了对单一存储库(也称为多包存储库、多项目存储库或单体存储库)的支持, 你可以创建一个 workspace 以将多个项目合并到一个仓库中。
一个 workspace 的根目录下必须有 pnpm-workspace.yaml 文件, 也可能会有 .npmrc 文件。
Workspace 协议 (workspace:)
默认情况下,如果可用的 packages 与已声明的可用范围相匹配,pnpm 将从工作区链接这些 packages。 例如,如果 bar 中有 "foo":"^1.0.0" 的这个依赖项,则 [email protected] 链接到 bar 。 但是,如果 bar 的依赖项中有 "foo": "2.0.0",而 [email protected] 在工作空间中并不存在,则将从 npm registry 安装 [email protected] 。 这种行为带来了一些不确定性。
幸运的是,pnpm 支持 workspace 协议 workspace: 。 当使用此协议时,pnpm 将拒绝解析除本地 workspace 包含的 package 之外的任何内容。 因此,如果设置为 "foo": "workspace:2.0.0" 时,安装将会失败,因为 "[email protected]" 不存在于此 workspace 中。
使用示例:
工作空间中存在以下项目:
+ packages/
+ foo/
+ bar/
+ qar/
+ zoo/如果各个项目以其目录名作为其 package name,那么可以在其他项目中如下引入依赖:
{
"dependencies": {
"foo": "workspace:*",
"bar": "workspace:~",
"qar": "workspace:^",
"zoo": "workspace:^1.5.0"
}
}提示
引入依赖的包名,是由包的 package.json name 确定,而不是 workspace 目录下的目录名确定。
发布 Workspace
当以上示例进行发布时,会被转换为
{
"dependencies": {
"foo": "1.5.0",
"bar": "~1.5.0",
"qar": "^1.5.0",
"zoo": "^1.5.0"
}
}这个功能允许你发布转化之后的包到远端,并且可以正常使用本地 workspace 中的 packages,而不需要其它中间步骤。包的使用者也可以像常规的包那样正常使用,且仍然可以受益于语义化版本。
