pnpm,英文里面的意思叫做 performant npm,意味“高性能的 npm”,官网地址 https://pnpm.io/
Pnpm 本质上就是一个包管理器,这一点跟 npm/yarn 没有区别,但它作为杀手锏的优势在于:
安装的依赖包文件,会通过硬链接(hard links)的形式存储在 pnpm 创建的 store 目录下,默认情况下全局只会有一个 store 目录 .pnpm-store,所有的项目都共用这一目录下的硬链接
node_modules创建非扁平化的 node_modules 文件夹

npm/yarn 如今的安装的 node_modules 都是拍平的形式,而 pnpm 默认是不拍平的,这能解决“幽灵依赖”,如果想要继续使用拍平的方式,也支持配置 .npmrc 中 hoist = true
对于 monorepo 类型的项目,pnpm 提供了 workspace 来支持,而且比 yarn workspace 配置更简单,只需在项目根目录下创建一个 pnpm-workspace.yaml 文件。具体可以参考官网文档
装过的包都通过 hard links 存储在磁盘的 .pnpm-store 文件夹内,所有项目共用,相同的包直接复用

不采用拍平的方式,绝不会下载重复的包
当更新一个包时,如果原版本有 100 个文件,新版本只新增了 1 个文件,pnpm 不会重新下载 101 个文件,而只是下载新增的 1 个文件
即 npm 包分身的问题
node_modules 采用循环链式依赖的方式进行安装,由此产生的问题:依赖链路过长,大量重复安装的依赖
将 npm 切换到 npm3 之前,新建一个项目进行测试:
配置该项目依赖两个包:fs-extra、jsonfile

执行 npm install 产生的 node_modules:

可以看到由于 fs-extra 和 jsonfile 都依赖了 graceful-fs、universalify,所以分别在两个依赖包下分别安装了一遍,这就是链式的循环依赖
实际开发中项目依赖的包会很多,这样链式嵌套的情况会导致链路十分冗长,且重复安装的包特别多
为了解决以上问题,npm3+ 和 yarn 采用了拍平 node_modules 的方式进行安装:拍平后依赖链路不再嵌套,不会安装重复的包
下面是上一个项目,使用 npm3+ 安装的 node_modules:

但因此产生的新问题 ——“幽灵依赖”
随着 monorepo 的趋势,yarn workspace 成为一种流行的实现方式,而这种和 npm3+ 相同的 node_modules 安装方式出现了新的问题:npm 包分身
同样的,新建一个基于 yarn workspace 的 monorepo 项目进行测试:
创建 4 个工作区,分别让 package1 和 package2 依赖 fs-extra@9,package3 和 package4 依赖 fs-extra@10
在项目根目录执行 yarn 安装的 node_modules 如下:

解决幽灵依赖,绝不会重复安装相同的包
每个 workspace 下都有自己的 node_modules,里面只能访问到在 package.json 里显示声明的 fs-extra,而 fs-extra 软链接到全局 node_modules 下对应版本的 fs-extra,没有任何一个包会安装多次

Phantom dependencies 幽灵依赖,解释起来很简单,即某个包并没有在 package.json 中声明依赖,却能在项目中导入使用
例如:
project 在 pacakge.json 里只依赖了 packageA,而 packageA 本身依赖了 packageBnode_modules 下将会同时有 packageA 和 packageB,且 project 中可直接 import packageBpackageA 不再依赖 packageB ,project 使用 packageB 这种做法将会报错这种幽灵依赖的做法在当前的开发环境下已是常见现象,而使用 pnpm,node_modules 不再拍平,将无法再使用幽灵依赖
npm install pnpm -g
新建一个文件夹作为项目目录(生成 package.json),在目录下执行指令(和 npm init 一致)
pnpm init
# 安装 package.json 所有依赖
pnpm install
# 安装单独包
pnpm add <package_name>
在项目下创建一个工作区的文件夹,例如 packages
在根目录创建一个 workspace 配置文件 pnpm-workspace.yaml,写入:
packages:
# 所有在 packages 子目录下的 package
- 'packages/**'
# 不包括在 test 文件夹下的 package
- '!**/test/**'
安装依赖:
# 根项目安装(Monorepo 全局)
pnpm add <package_name> -W
# 某个子项目安装
pnpm add <package_name> --filter <workspace_name>
运行脚本:
pnpm start --filter <workspace_name>
删除全局和每个 workspace 的 node_modules:
pnpm -r exec rm -rf node_modules
更多使用方式查看官网
pnpm 作为包管理器里面的“后起之秀”,通过作者别出心裁的设计方案,完美解决了许多了现有的包管理工具 npm、yarn 以及 node_modules 本身设计原因留下的痛点