乐趣区

关于前端:pnpm才是前端工程化项目的未来

前言

置信小伙伴们都接触过 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

为了解决上述问题,npm3yarn 都抉择了扁平化构造,也就是说当初咱们看到的 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

后面说了那么多的 npmyarn的毛病,当初再来看看 pnpm 是如何解决这些难堪问题的。

什么是 pnpm

疾速的,节俭磁盘空间的包管理工具

就这么简略,说白了它跟 npmyarn没有区别,都是包管理工具。但它的独特之处在于:

  • 包装置速度极快
  • 磁盘空间利用十分高效

个性

安装包速度快

从上图能够看出,pnpm的包装置速度显著快于其它包管理工具。那么它为什么会比其它包管理工具快呢?

咱们来能够来看一下各自的装置流程

  • npm/yarn
  1. resolving:首先他们会解析依赖树,决定要 fetch 哪些安装包。
  2. fetching:装置去 fetch 依赖的 tar 包。这个阶段能够同时下载多个,来减少速度。
  3. 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 中的包反复问题。

咱们别离用 npmpnpm来装置 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.0B@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 中去了,storepnpm 在硬盘上的公共存储空间。

pnpmstore 在 Mac/linux 中默认会设置到{home dir}>/.pnpm-store/v3;windows 下会设置到以后盘符的根目录下。应用名为 .pnpm-store 的文件夹名称。

我的项目中所有 .pnpm/ 依赖名 @版本号 /node_modules/ 下的软连贯都会连贯到 pnpmstore中去。

原文首发地址点这里,欢送大家关注公众号 「前端南玖」,如果你想进前端交换群一起学习,请点这里

我是南玖,咱们下期见!!!

退出移动版