共计 4080 个字符,预计需要花费 11 分钟才能阅读完成。
前言
置信小伙伴们都接触过 npm/yarn
,这两种包管理工具想必是大家工作中用的最多的包管理工具,npm
作为 node
官网的包管理工具,它是随着 node 的诞生一起呈现在大家的视线中,而 yarn
的呈现则是为了解决 npm
带来的诸多问题,尽管 yarn
进步了依赖包的装置速度与应用体验,但它仍旧没有解决 npm
的依赖反复装置等致命问题。pnpm的呈现完满解决了依赖包反复装置的问题,并且实现了 yarn
带来的所有优良体验,所以说pnpm 才是前端工程化我的项目的将来。
如果这篇文章有帮忙到你,❤️关注 + 点赞❤️激励一下作者,文章公众号首发,关注 前端南玖
第一工夫获取最新文章~
npm 与 yarn 存在的问题
晚期的 npm
在 npm\@3 之前,node_modules
构造能够说是 整洁
、 可预测
的,因为过后的依赖构造是这样的:
node_modules
└─ 依赖 A
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖 B
├─ index.js
└─ package.json
└─ 依赖 C
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖 B
├─ index.js
└─ package.json
每个依赖上面都保护着本人的node_modules
,这样看起来的确十分整洁,但同时也带来一些较为重大的问题:
- 依赖包反复装置
- 依赖层级过多
- 模块实例无奈共享
依赖包反复装置
从下面的依赖构造咱们能够看出,依赖 A 与依赖 C 同时援用了依赖 B,此时的依赖 B 会被下载两次。此刻咱们想想要是某一个依赖被援用了 n 次,那么它就须要被下载 n 次。(此时心里是不是在想,怎么会有如此坑的设计)
依赖层级过多
咱们再来看另外一种依赖构造:
node_modules
└─ 依赖 A
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖 B
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖 C
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖 D
├─ index.js
└─ package.json
这种依赖层级少还能承受,要是依赖层级多了,这样一层一层嵌套上来,就像一个依赖天堂,不利于保护。
npm\@3 与 yarn
为了解决上述问题,npm3
与 yarn
都抉择了扁平化构造,也就是说当初咱们看到的 node_modules
外面的构造不再有依赖嵌套了,都是如下依赖构造:
node_modules
└─ 依赖 A
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖 C
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖 B
├─ index.js
├─ package.json
└─ node_modules
node_modules
下所有的依赖都会平铺到同一层级。因为 require 寻找包的机制,如果 A 和 C 都依赖了 B,那么 A 和 C 在本人的 node\_modules 中未找到依赖 C 的时候会向上寻找,并最终在与他们同级的 node\_modules 中找到依赖包 C。这样 就不会呈现反复下载的状况。而且依赖层级嵌套也不会太深。因为没有反复的下载,所有的 A 和 C 都会寻找并依赖于同一个 B 包。天然也就解决了实例无奈共享数据的问题
因为这个扁平化构造的特点,想必大家都遇到了这样的体验,本人明明就只装置了一个依赖包,关上 node_modules
文件夹一看,外面却有一大堆。
这种扁平化构造尽管是解决了之前的嵌套问题,但同时也带来了另外一些问题:
- 依赖构造的不确定性
- 扁平化算法的复杂度减少
- 我的项目中依然能够非法拜访没有申明过的依赖包(幽灵依赖)
依赖构造的不确定性
这个怎么了解,为什么会产生这种问题呢?咱们来认真想想,退出有如下一种依赖构造:
A 包与 B 包同时依赖了 C 包的不同版本,因为同一目录下不能呈现两个同名文件,所以这种状况下同一层级只能存在一个版本的包,另外一个版本还是要被嵌套依赖。
那么问题又来了,既然是要一个扁平化一个嵌套,那么这时候是如何确定哪一个扁平化哪一个嵌套的呢?
这两种构造都有可能,精确点说 哪个版本的包被晋升,取决于包的装置程序!
这就是为什么会产生依赖构造的 不确定
问题,也是 lock 文件
诞生的起因,无论是 package-lock.json
(npm 5.x 才呈现) 还是 yarn.lock
,都是为了保障 install 之后都产生确定的node_modules
构造。
尽管如此,npm/yarn 自身还是存在 扁平化算法简单
和package 非法拜访
的问题,影响性能和平安。
pnpm
后面说了那么多的 npm
与yarn
的毛病,当初再来看看 pnpm 是如何解决这些难堪问题的。
什么是 pnpm
疾速的,节俭磁盘空间的包管理工具
就这么简略,说白了它跟 npm
与yarn
没有区别,都是包管理工具。但它的独特之处在于:
- 包装置速度极快
- 磁盘空间利用十分高效
个性
安装包速度快
从上图能够看出,pnpm
的包装置速度显著快于其它包管理工具。那么它为什么会比其它包管理工具快呢?
咱们来能够来看一下各自的装置流程
- npm/yarn
- resolving:首先他们会解析依赖树,决定要 fetch 哪些安装包。
- fetching:装置去 fetch 依赖的 tar 包。这个阶段能够同时下载多个,来减少速度。
- wrting:而后解压包,依据文件构建出真正的依赖树,这个阶段须要大量文件 IO 操作。
- pnpm
上图是 pnpm 的装置流程,能够看到针对每个包的三个流程都是平行的,所以速度会快很多。当然 pnpm 会多一个阶段,就是通过链接组织起真正的依赖树目录构造。
磁盘空间利用十分高效
pnpm 外部应用 基于内容寻址
的文件系统来存储磁盘上所有的文件,这个文件系统杰出的中央在于:
- 不会反复装置同一个包。用 npm/yarn 的时候,如果 100 个我的项目都依赖 lodash,那么 lodash 很可能就被装置了 100 次,磁盘中就有 100 个中央写入了这部分代码。但在应用 pnpm 只会装置一次,磁盘中只有一个中央写入,前面再次应用都会间接应用
hardlink
。 - 即便一个包的不同版本,pnpm 也会极大水平地复用之前版本的代码。举个例子,比方 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会从新写入 101 个文件,而是保留原来的 100 个文件的
hardlink
,仅仅写入那一个新增的文件
。
反对 monorepo
pnpm 与 npm/yarn 另外一个很大的不同就是反对了 monorepo,pnpm 内置了对 monorepo 的反对,只需在工作空间的根目录创立 pnpm-workspace.yaml 和.npmrc 配置文件,同时还反对多种配置,相比拟 lerna 和 yarn workspace,pnpm 解决 monorepo 的同时,也解决了传统计划引入的问题。
monorepo 的主旨就是用一个 git 仓库来治理多个子项目,所有的子项目都寄存在根目录的
packages
目录下,那么一个子项目就代表一个package
。
依赖治理
pnpm 应用的是 npm version 2.x 相似的嵌套构造,同时应用.pnpm 以平铺的模式贮存着所有的包。而后应用 Store + Links 和文件资源进行关联。简略说 pnpm 把会包下载到一个公共目录,如果某个依赖在 sotre 目录中存在了话,那么就会间接从 store 目录外面去 hard-link,防止了二次装置带来的工夫耗费,如果依赖在 store 目录外面不存在的话,就会去下载一次。通过 Store + hard link 的形式,使得我的项目中不存在 NPM 依赖天堂问题,从而完满解决了 npm3+ 和 yarn 中的包反复问题。
咱们别离用 npm
与pnpm
来装置 vite 比照看一下
npm | pnpm |
---|---|
所有依赖包平铺在 node_modules 目录,包含间接依赖包以及其余次级依赖包 |
node_modules 目录下只有 .pnpm 和间接依赖包,没有其余次级依赖包 |
没有符号链接(软链接) | 间接依赖包的前面有符号链接(软链接)的标识 |
pnpm 装置的vite
所有的依赖都软链至了 node_modules/.pnpm/
中的对应目录。把 vite
的依赖搁置在同一级别防止了循环的软链。
软链接 和 硬链接 机制
pnpm 是通过 hardlink 在全局外面搞个 store 目录来存储 node\_modules 依赖外面的 hard link 地址,而后在援用依赖的时候则是通过 symlink 去找到对应虚构磁盘目录下 (.pnpm 目录) 的依赖地址。
这两者联合在一起工作之后,如果有一个我的项目依赖了 A@1.0.0
和 B@1.0.0
,那么最初的 node\_modules 构造出现进去的依赖构造可能会是这样的:
node_modules
└── A // symlink to .pnpm/A@1.0.0/node_modules/A
└── B // symlink to .pnpm/B@1.0.0/node_modules/B
└── .pnpm
├── A@1.0.0
│ └── node_modules
│ └── A -> <store>/A
│ ├── index.js
│ └── package.json
└── B@1.0.0
└── node_modules
└── B -> <store>/B
├── index.js
└── package.json
node_modules
中的 A 和 B 两个目录会软连贯到 .pnpm 这个目录下的实在依赖中,而这些实在依赖则是通过 hard link 存储到全局的 store 目录中。
store
pnpm
下载的依赖全副都存储到 store
中去了,store
是 pnpm
在硬盘上的公共存储空间。
pnpm
的 store
在 Mac/linux 中默认会设置到{home dir}>/.pnpm-store/v3
;windows 下会设置到以后盘符的根目录下。应用名为 .pnpm-store 的文件夹名称。
我的项目中所有 .pnpm/ 依赖名 @版本号 /node_modules/
下的软连贯都会连贯到 pnpm
的store
中去。
原文首发地址点这里,欢送大家关注公众号 「前端南玖」,如果你想进前端交换群一起学习,请点这里
我是南玖,咱们下期见!!!