pnpm 是怎么推翻 npm 和 yarn 的?
今日研讨了一下 pnpm
的机制,发现它的确很强壮,乃至能够说对 yarn
和 npm
形成了降维冲击 。
咱们从包办理工具的开展前史,一同看下究竟好在哪里?
npm2
在 npm 3.0 版别之前,项目的 node_modules
会呈现出嵌套结构,也便是说,我装置的依靠、依靠的依靠、依靠的依靠的依靠...,都是递归嵌套的
node_modules
├─ express
│ ├─ index.js
│ ├─ package.json
│ └─ node_modules
│ ├─ accepts
│ │ ├─ index.js
│ │ ├─ package.json
│ │ └─ node_modules
│ │ ├─ mime-types
| | | └─ node_modules
| | | └─ mime-db
| │ └─ negotiator
│ ├─ array-flatten
│ ├─ ...
│ └─ ...
└─ A
├─ index.js
├─ package.json
└─ node_modules
└─ accepts
├─ index.js
├─ package.json
└─ node_modules
├─ mime-types
| └─ node_modules
| └─ mime-db
└─ negotiator
规划缺点
这种嵌套依靠树的规划的确存在几个严峻的问题
- 途径过长问题: 因为包的嵌套结构 ,
node_modules
的目录结构可能会变得十分深,乃至可能会超出体系途径长度上限 ,究竟 windows 体系的文件途径默许最多支撑 256 个字符 - 磁盘空间糟蹋: 多个包之间不免会有公共的依靠,公共依靠会被屡次装置在不同的包目录下,导致磁盘空间被很多糟蹋 。比方上面
express
和 A 都依靠了accepts
,它就被装置了两次 - 装置速度慢:因为依靠包之间的嵌套结构,
npm
在装置包时需求屡次处理和下载相同的包,导致装置速度变慢,尤其是在依靠联系杂乱的项目中
其时 npm 还没处理这些问题, 社区便推出了新的处理计划 ,便是 yarn。 它引入了一种新的依靠办理方法——扁平化依靠。
看到 yarn 的成功,npm 在 3.0 版别中也引入了相似的扁平化依靠结构
yarn
yarn 的首要改善之一便是经过扁平化依靠结构来处理嵌套依靠树的问题,具体来说
铺平,yarn 尽量将一切依靠包装置在项目的顶层 node_modules
目录下,而不是嵌套在各自的 node_modules
目录中。
这样一来,减少了目录的深度,避免了途径过长的问题 ,也尽可能避免了依靠被屡次重复装置的问题
咱们能够在 yarn-example 看到整个目录,悉数铺平在了顶层 node_modules
目录下,打开下面的包大部分是没有二层 node_modules
的
可是,有些依靠包仍是会在自己的目录下有一个 node_modules
文件夹,呈现嵌套的状况,例如 yarn-example 下的http-errors
依靠包就有自己的 node_modules
,原因是:
当一个项目的多个依靠包需求同一个库的不同版别时,yarn 只能将一个版别的库提高到顶层 node_modules
目录中。 关于需求这个库其他版别的依靠,yarn 仍然需求在这些依靠包的目录下创立一个嵌套的 node_modules
来寄存不同版别的包
比方,包 A 依靠于 [email protected]
,而包 B 依靠于 [email protected]
。因为这两个版别的 lodash
不能兼并,yarn
会将 [email protected]
提高到顶层 node_modules
,而 [email protected]
则被嵌套在包 B 的 node_modules
目录下。
鬼魂依靠
尽管 yarn 和 npm 都采用了扁平化的计划来处理依靠嵌套的问题,但这种计划自身也有一些缺点,其间鬼魂依靠是一个首要问题。
鬼魂依靠,也便是你分明没有在 package.json
文件中声明的依靠项,但在项目代码里却能够 require
进来
这个也很简单了解,因为依靠的依靠被扁平化装置在顶层 node_modules
中,所以咱们能拜访到依靠的依靠
可是这样是有危险的,因为没有显式依靠,未来某个时分这些包可能会因为某些原因消失(例如新版别库不再引证这个包了,然后咱们更新了库),就会引发代码运转过错
糟蹋磁盘空间
而且还有一个问题,便是上面说到的依靠包有多个版别的时分,只会提高一个,那其他版别的包不仍是仿制了很屡次么,仍然有糟蹋磁盘空间的问题
那社区有没有处理这俩问题的思路呢? pnpm 便是其间最成功的一个
pnpm
pnpm 经过大局存储和符号链接机制从本源上处理了依靠重复装置和途径长度问题,一起也避免了扁平化依靠结构带来的鬼魂依靠问题
pnpm 的优势归纳来说便是“快、准、狠”:
- 快:装置速度快
- 准:装置过的依靠会精确复用缓存,乃至包版别晋级带来的改变都只 diff,绝不糟蹋一点空间
- 狠:直接废掉了鬼魂依靠
履行 npm add express
,咱们能够在 pnpm-example 看到整个目录,因为只装置了 express
,那 node_modules
下就只有 express
那么一切的(次级)依靠去哪了呢? binggo,在node_modules/.pnpm/
目录下,.pnpm/
以平铺的方法储存着一切的包
三层寻址
- 一切 npm 包都装置在大局目录
~/.pnpm-store/v3/files
下,同一版别的包仅存储一份内容,乃至不同版别的包也仅存储 diff 内容。 - 顶层
node_modules
下有.pnpm
目录以打平结构办理每个版别包的源码内容,以硬链接方法指向 pnpm-store 中的文件地址。 - 每个项目
node_modules
下装置的包以软链接方法将内容指向node_modules/.pnpm
中的包。
所以每个包的寻觅都要经过三层结构:node_modules/package-a
> 软链接 node_modules/.pnpm/[email protected]/node_modules/package-a
> 硬链接 ~/.pnpm-store/v3/files/00/xxxxxx
。
这便是 pnpm 的完成原理。官方给了一张原理图,能够调配食用
前面说过,npm 包都被装置在大局 pnpm store
,默许状况下,会创立多个存储(每个驱动器(盘符)一个),并在项目地点盘符的根目录
所以,同一个盘符下的不同项目,都能够共用同一个大局 pnpm store
,绝绝子啊👏,大大节省了磁盘空间,提高了装置速度
软硬链接
也便是说,一切的依靠都是从大局 store 硬衔接到了 node_modules/.pnpm
下,然后之间经过软链接来相互依靠。
那么,这儿的软衔接、硬链接究竟是什么东西?
硬链接是指向磁盘上原始文件地点的同一方位 (直接指向相同的数据块)
软衔接能够了解为新建一个文件,它包括一个指向另一个文件或目录的途径 (指向方针途径)
.npmrc
shamefully-hoist
,默许 false
- false:
node_modules
下只能看到直接依靠的套件,次级依靠在node_modules/.pnpm
目录下;无法拜访其他子包部分装置的依靠项,例如,vue-dome2 装置的 lodash,vue-dome1 是拜访不到的 - true:將一切套件都拉升到
node_modules
目錄下,能拜访到其他子包部分装置的依靠项,例如,vue-dome2 装置的 lodash,vue-dome1 是能拜访到的
// .npmrc
# pnpm 装备
shamefully-hoist=false
总结
npm2 的嵌套结构: 每个依靠项都会有自己的 node_modules
目录,导致了依靠被重复装置,严峻糟蹋了磁盘空间💣;在依靠层级比较深的项目中,乃至会超出 windows 体系的文件途径长度💣
npm3+ 和 Yarn 的扁平化战略: 尽量将一切依靠包装置在项目的顶层 node_modules
目录下,处理了 npm2
嵌套依靠的问题。可是该计划有一个严重缺点便是“鬼魂依靠”💣;而且依靠包有多个版别时,只会提高一个,那其他版别仍然会被重复装置,仍是有糟蹋磁盘空间的问题💣
pnpm大局存储和符号链接机制: 结合软硬链和三层寻址,处理了依靠被重复装置的问题,愈加反常的是,同一盘符下的不同项目都能够共用一个大局 pnpm store
。节省了磁盘空间,而且底子不存在“鬼魂依靠”,装置速度还贼快💪💪💪
参阅文档
weekly/前沿技术/253.精读《pnpm》
pnpm 是凭什么对 npm 和 yarn 降维冲击的)
平铺的结构不是 node_modules 的仅有完成方法 | pnpm中文网