Pnpm vs npm vs yarn

99 阅读5分钟

参考:www.dhiwise.com/post/pnpm-v…

juejin.cn

带着疑问往下看:

  1. 软连接和硬链接有什么区别?
  2. 循环依赖怎么解决?maples7.com/2016/08/17/…

es6.ruanyifeng.com/#docs/modul…

  1. 幽灵依赖是什么?版本依赖管理(antd 与 @ant-design/cssinjs :github.com/ant-design/…

一、npm/yarn

npm/yarn install之后,首先会构建依赖树,然后针对每个节点下的包,会经历下面四个步骤:

  1. 将依赖包的版本区间解析为某个具体的版本号

  2. 下载对应版本依赖的 tar 包到本地离线镜像

  3. 将依赖从离线镜像解压到本地缓存

  4. 将依赖从缓存拷贝到当前目录的 node_modules 目录

(1)早年npm1npm2 中呈现出的是嵌套结构

node_modules
└─ foo
   ├─ index.js
   ├─ package.json
   └─ node_modules
      └─ bar
         ├─ index.js
         └─ package.json

存在什么问题:

  1. 依赖层级太深,会导致文件路径过长的问题,尤其在 window 系统下。

  2. 大量重复的包被安装,文件体积超级大。比如跟 foo 同级目录下有一个baz,两者都依赖于同一个版本的lodash,那么 lodash 会分别在两者的 node_modules 中被安装,也就是重复安装。

  3. 模块实例不能共享。比如 React 有一些内部变量,在两个不同包引入的 React 不是同一个模块实例,因此无法共享内部变量,导致一些不可预知的 bug。

facebook的yarn首次采用了 扁平化结构去解决,嵌套过深的问题。npm3也开始学习yarn的思路。

node_modules
├─ foo
|  ├─ index.js
|  └─ package.json
└─ bar
   ├─ index.js
   └─ package.json

扁平化的缺点:

  1. 依赖结构的不确定性juejin.cn/post/693204…

  2. 扁平化算法本身的复杂性很高,耗时较长。

  3. 幽灵依赖:项目中可以非法访问没有声明过依赖的包。

二、硬链接(Hard Link)和 符号链接(Symbolic Link, 又称软连接)

参考:

简单说明:www.runoob.com/note/29134

详细场景:www.51cto.com/article/667…

硬连接(Hard Link)

硬连接指通过索引节点inode 来进行连接。在 Linux 的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。在 Linux 中,多个文件名指向同一索引节点是存在的。比如:A 是 B 的硬链接(A 和 B 都是文件名),则 A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号相同,即一个 inode 节点对应两个不同的文件名,两个文件名指向同一个文件,A 和 B 对文件系统来说是完全平等的。删除其中任何一个都不会影响另外一个的访问。

硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。

硬链接存在 2 个限制:

不允许用户给目录创建硬链接,即:用户不可以,操作系统可以(比如:每个目录下的 . 和 ..);

只有在同一个文件系统中的文件,才能创建硬链接,也就是说:不能跨文件系统。

为了克服硬链接的 2 个限制,软链接被引入进来了!

符号链接(Symbolic Link, 又称软连接)

软链接文件的内容是一个文本字符串,存储的是目标文件(即:链接到的文件)的路径名。

软链接文件有类似于 Windows 的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。比如:A 是 B 的软链接(A 和 B 都是文件名),A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号不相同,A 和 B 指向的是两个不同的 inode,继而指向两块不同的数据块。但是 A 的数据块中存放的只是 B 的路径名(可以根据这个找到 B 的目录项)。A 和 B 之间是“主从”关系,如果 B 被删除了,A 仍然存在(因为两个是不同的文件),但指向的是一个无效的链接。

三、pnpm

(一)节省磁盘空间(全局的仓库管理)

使用 npm 时,依赖每次被不同的项目使用,都会重复安装一次。 而在使用 pnpm 时,依赖会被存储在内容可寻址的存储中,所以:

  1. 如果你用到了某依赖项的不同版本,只会将不同版本间有差异的文件添加到仓库。 For instance, if it has 100 files, and a new version has a change in only one of those files, pnpm update will only add 1 new file to the store, instead of cloning the entire dependency just for the singular change.
  2. 所有文件都会存储在硬盘上的某一位置。 当软件包被被安装时,包里的文件会硬链接到这一位置,而不会占用额外的磁盘空间。 这允许你跨项目地共享同一版本的依赖。

因此,您在磁盘上节省了大量空间,这与项目和依赖项的数量成正比,并且安装速度要快得多!

(二)提高安装速度

  1. 依赖解析(resolving)。 仓库中没有的依赖都被识别并获取到仓库。
  2. 目录结构计算。 The node_modules directory structure is calculated based on the dependencies.
  3. 链接依赖项(linking)。 All remaining dependencies are fetched and hard linked from the store to node_modules.