妈妈再也不用担心面试官问我pnpm了

58 阅读6分钟

目前前端包管理工具有npm,cnpm,yarn,pnpm;我们经常使用这些工具加上相应的命令去做一些操作,比如 npm install npm run devyarn add 但我们会发现这些命令执行的速度完全不一样,目前最快的应该是 pnpm,这究竟是什么原因呢,于是我想通过这篇文章讲清楚

npm

随着 Node.js 的推出,npm(Node Package Manager)于 2010 年被引入,成为 Node.js 生态系统中分享和管理模块的标准方式,node_modules 目录用于局部安装依赖。

npm 的 包的版本锁定文件是 package-lock.json

npm2 及以前的嵌套依赖模型

每个包将其依赖安装在自己的 node_modules 目录下,形成嵌套结构,解决了版本冲突、依赖隔离等问题,但存在磁盘空间占用大、深层嵌套问题和安装更新缓慢等致命缺点。

npm3 架构升级

为解决上述问题,npm 在第三个版本进行了重构,通过将依赖扁平化,减少了重复的包版本和项目总体积,同时避免了早期的深层嵌套问题,但仍可能存在嵌套问题,因为根目录只能存放某个包的一个版本。

cnpm

是npm的一个镜像版本,由淘宝团队创建和维护。它解决了中国开发者在使用官方npm时遇到的速度慢、连接不稳定的问题。

cnpm 不生成 版本锁定 lock 文件,也不会识别项目下的 lock 文件,所以还是推荐使用 npm 或者其他包管理工具,通过绑定镜像源的方式来管理项目的包。

yarn

Yarn 是由 Facebook、Google、Exponent 和 Tilde 共同开发的包管理工具,最初发布于2016年10月。它的设计目标是解决 npm 在安装依赖项时存在的速度慢、不一致性和安全性问题。

yarn的特点

  1. 速度提升
    • Yarn 使用并行化下载来加速依赖的安装过程。
    • 缓存所有下载过的包,这样在不同的项目中再次使用相同的包时,可以立即从本地缓存中恢复,无需重新下载
  1. 安全性
    • Yarn 在安装依赖时会校验包的完整性,通过检查包的哈希值来防止恶意软件或篡改的包被安装
  1. 一致性
    • Yarn 引入了 yarn.lock 文件来锁定项目的依赖树,确保每个开发者和环境都能得到完全相同的依赖版本,避免了“在我的机器上工作”的问题。

yarn相比npm已经有非常大的提升了,但是有一个问题始终无法处理,那就是幽灵依赖

幽灵依赖

是指在项目中存在但实际上并没有被直接使用的依赖项。

我们假设 B 并没有在 package.json 中注册,但由于 A 依赖 B,B会被提取到 node_moduls 顶层,那么在项目中就可以直接引用 B,这就是幽灵依赖,当A依赖更新升级时,会有一些隐藏问题

  1. 代码可读性很低,B依赖是可以在项目中直接使用的 import B from B,但是package中并没有标注,会出现一些问题
  2. 版本问题:A更新升级了B,升级后的B不具有某个方法时,将会导致一些报错

为了解决这类幽灵依赖,于是有了pnpm

pnpm

pnpm(performant npm)旨在解决 npm 和 yarn 在某些方面存在的效率和存储问题,同时通过引入一种独特的链接方式有效地解决了大部分幽灵依赖的问题。

硬连接

是指向文件系统中的一个已存在的文件的直接引用。与符号链接(软链接)不同,硬链接不是创建一个新的文件,而是为现有的文件提供了一个额外的访问路径或入口。这意味着多个硬链接可以指向同一个文件内容,而这些硬链接在文件系统中看起来就像独立的文件一样。

有以下特点:

  1. 共享文件数据
  • 所有硬链接和原始文件都共享相同的数据块。因此,对通过任何一个硬链接或原始文件名进行的修改都会反映到所有其他硬链接上。
  1. 不依赖源文件名
  • 硬链接并不包含指向原始文件的路径信息,它只是指向文件的数据块。因此,即使原始文件被重命名或删除,只要还有至少一个硬链接存在,文件的内容仍然可以访问
  1. 不能跨文件系统
  • 硬链接只能在同一文件系统内部创建。你不能在不同的磁盘分区或网络驱动器之间创建硬链接

软连接

文件系统中的一个特殊类型的文件,它包含了指向另一个文件或目录的路径。与硬链接不同,符号链接并不直接引用文件的数据块,而是通过路径名来间接引用目标文件或目录。符号链接可以跨越不同的文件系统,并且可以指向不存在的文件或目录(即所谓的“悬空链接”)

有以下特点

  1. 支持跨文件系统
  2. 间接引用
  • 符号链接包含的是目标文件或目录的路径,而不是直接指向文件的数据块。因此,符号链接可以存在于与目标文件不同的文件系统上

存储效率

npm/yarn 占用了太多的磁盘空间,所以如果你安装同一个包100次,100分钟的包会保存到node_modules磁盘上。 在日常示例中,如果前一个项目已结束并且 node_modules 保持不变,则通常会占用大量磁盘空间。

pnpm针对这个问题采用了硬连接方式

pnpm 将包存储在同一个文件夹(Content-Addressable Store)中,并在您再次安装同一个包时创建一个硬链接。 MacOs 的默认位置是 ~/.pnpm-store。 此外,同一包的不同版本将仅保存差异。

pnpm对幽灵依赖的处理

node_modules最外层只包含package.json中所有的依赖,这样可以使的页面引用不存在的依赖报错

.pnpm/ 以平铺的形式储存着所有的包,所以每个包都可以在这种命名模式的文件夹中被找到:.pnpm/<name>@<version>/node_modules/

pnpm怎么找包

项目中通过 import XX from 'XX',真正的寻址是:

  1. 去项目最外层的node_modules找到 XX
  2. 通过XX的软连接进入.pnpm/<name>@<version>/node_modules/
  3. 通过硬连接去 .pnpm-store 中找到依赖的数据地址

总结

pnpm为什么这么快?总的来说pnpm处理了两件事,第一依赖共享,通过.pnpm-store全局存储不同种类,不同版本的依赖项,这样在其他的项目下面不需要重新下载;第二是独特的连接方式,通过软硬连接处理多层找包的方式兼顾了速度同时处理了幽灵依赖