【依赖】pnpm、yarn、npm

275 阅读7分钟

pnpm

前置知识

符号链接/软连接(symbolic link)

以下解释来自百度

符号链接软链接)是一类特殊的文件, 其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用。[1] 符号链接最早在4.2BSD版本中出现(1983年)。今天POSIX操作系统标准、大多数类Unix系统Windows VistaWindows 7都支持符号链接。Windows 2000Windows XP在某种程度上也支持符号链接。

其实也可以理解为快捷方式,在windows 下面右键文件可以创建,也可以使用linux 命令

 touch text.text
// ln [参数][源文件或目录][目标文件或目录]
// -s 代表创建软连接,不加代表创建硬连接
ln -s test.text test_symbolic_link.txt

image.png

硬链接

以下解释来自百度

硬链接(hard link,也称链接)就是一个文件的一个或多个文件名。再说白点,所谓链接无非是把文件名和计算机文件系统使用的节点号链接起来。因此我们可以用多个文件名与同一个文件进行链接,这些文件名可以在同一目录或不同目录。

ln test.text test_hard_link.txt

使用 ls -i 可以看到文件的(文件系统中的物理索引(也称为inode)

image.png

硬链接和源文件的inode 是同一个

硬链接和软链接的区别

软链接

  • 1.软链接,以路径的形式存在。类似于Windows操作系统中的快捷方式
  • 2.软链接可以跨文件系统(FAT32、NTFS...) ,硬链接不可以
  • 3.软链接可以对一个不存在的文件名进行链接
  • 4.软链接可以对目录进行链接

硬链接

  • 1.硬链接,以文件副本的形式存在。但不占用实际空间(存疑??实际上我看到还有占了空间)

image.png

  • 2.不允许给目录创建硬链接
  • 3.硬链接只有在同一个文件系统中才能创建

内容可寻址存储(Contenct-Addressable Store,CAS)

是一种存储信息的方式,与此对应的还有固定内容存储(FAS)

内容可寻址存储,也称为内容寻址存储或缩写为CAS,是一种存储信息的方式,因此可以根据其内容而不是其位置来检索信息。它已被用于高速存储和检索的固定内容,如存储,符合政府规定的文件。内容可寻址存储类似于内容可寻址内存。 具体就不展开了,只要理解两个店,他是一种存储信息的方式,常使用加密哈希函数从文档生成的摘要,以标识存储系统中的该文档

使用pnpm的好处

节省磁盘空间

当使用npm时,如果你有100个项目使用一个依赖关系,你将有100份该依赖关系的副本保存在磁盘上。使用pnpm,该依赖关系将被存储在一个内容可寻址的存储器中,因此。

如果你依赖不同版本的依赖关系,只有不同的文件会被添加到存储区。例如,如果它有100个文件,而新版本只对其中的一个文件进行了修改,那么pnpm update将只向存储区添加一个新文件,而不是仅仅为了这个单一的修改而克隆整个依赖关系。

所有的文件都保存在磁盘上的一个地方。当软件包被安装时,它们的文件被硬链接到那个地方,不消耗额外的磁盘空间。这使得你可以在不同的项目中共享同一版本的依赖关系。

因此,你可以在磁盘上节省大量与项目和依赖关系数量成比例的空间,而且你的安装速度也会快很多

以上摘自官网,我理解是pnpmyarnnpm快的原因就是所有项目的依赖在一个store, 如果这个store里面有的话,则直接用,没有的话则从远程下载,这是快和节省空间的原因

那么问题来了这个store是在哪里,下面会给出答案

加快安装速度

  • 依赖性解析。所有需要的依赖被识别,并被提取到store
  • 目录结构计算。根据依赖关系计算 node_modules 目录结构。
  • 链接依赖项。所有剩余的依赖被获取并从store硬链接到node_modules

pnpm 究竟怎么管理依赖的

首先我们明白几个点, node_modules 下面究竟有哪些部分。以下以一个nextjs demo举例

pnpm 的 node_modules 布局使用符号链接来创建依赖项的嵌套结构

image.png

pnpm虚拟目录

image.png

以平铺(是不是和npm3.x有点像,注意是平铺在.pnpm下面)的形式项目所有的依赖包(包括间接依赖,每个依赖包都可以通过.pnpm/<name>@<version>/node_modules/<name>路径找到实际位置

.pnpm/array-name@2.1.0/node_modules/array-union

image.png

还是以官网的例子,真实demo中稍微比这复杂(比如上图有些包名为何是两个包甚至多个包名合起来的??)。 假设我们安装了直接依赖foo@1.0.0bar@1.0.0qar@2.0.0foo@1.0.0的依赖, qar@2.0.0 foo@1.0.0 的依赖

node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo   // 直接依赖。符号链接到./.pnpm/foo@1.0.0/node_modules/foo 
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       ├── bar -> <store>    // 包中的每个文件都硬链接到pnpm store中的对应文件,即<pnpm store path>/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar // 间接依赖qar,符号链接到../../qar@2.0.0/node_modules/qar 
    ├── foo@1.0.0
    │   └── node_modules
    │       ├── foo -> <store>/foo
    │       ├── bar -> ../../bar@1.0.0/node_modules/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar
    └── qar@2.0.0
        └── node_modules
            └── qar -> <store>/qar

.pnpm 是虚拟存储目录。再来看这个图就很好理解了

我觉得这BEFE这个总结很好

总结:pnpm使用符号链接Symbolic link(软链接)来创建依赖项的嵌套结构,将项目的直接依赖符号链接到node_modules的根目录,直接依赖的实际位置在.pnpm/<name>@<version>/node_modules/<name>,依赖包中的每个文件再硬链接(Hard link).pnpm store

pnpm store

pnpm store path  // pnpm store的存储路径

image.png

终于看到了庐山真面目了,原来真正的文件在这里。注意

  • Content-addressable store 使用 pnpm store path 可查看
  • Virtual store 也就是.pnpm 目录

pnpm install 安装的时候, 你会看到以下提示

packages are hard linked form the contenct-addressable store to the virtual store

关于pnpm store 的一些命令

  • pnpm status

查看 store 中已修改的包。

  • pnpm add

功能上等同于 pnpm add,不同之处在于它只把包加入存储中,且没有修改存储外的任何项目或文件。

  • pnpm prune

删除未被引用的包

重点是这个命令,因为store随着项目的增多,肯定会越来越多,那么如何保证不庞大呢。 删除未被引用的包, 因为

pnpm install 期间,包 foo@1.0.0 被更新为 foo@1.0.1。 pnpm 将在存储中保留 foo@1.0.0 ,因为它不会自动除去包。 如果包 foo@1.0.0 没有被其他任何项目使用,它将变为未引用。 运行 pnpm store prune 将会把 foo@1.0.0 从存储中删除 。

但还是要注意不要经常清理(我运行的时候,电脑风扇会响)

运行 pnpm store prune 是无害的,对您的项目没有副作用。 如果以后的安装需要已经被删除的包,pnpm 将重新下载他们。 最好的做法是 pnpm store prune 来清理存储,但不要太频繁。 有时,未引用的包会再次被需要。 这可能在切换分支和安装旧的依赖项时发生,在这种情况下,pnpm 需要重新下载所有删除的包,会暂时减慢安装过程

pnpm如何管理 peer dependencies

还是以官网的例子来说,foo@1.0.0 被安装在 foo-parent-1foo-parent-2中,他们都依赖于 barbaz, 不同的是 baz 版本不一致,一个是 baz@1.0.0 baz@1.1.0

- foo-parent-1  
- bar@1.0.0  
- baz@1.0.0  
- foo@1.0.0  
- foo-parent-2  
- bar@1.0.0  
- baz@1.1.0  
- foo@1.0.0

他们会有不同的组合,

node_modules
└── .pnpm
    ├── foo@1.0.0_bar@1.0.0+baz@1.0.0
    │   └── node_modules
    │       ├── foo
    │       ├── bar   -> ../../bar@1.0.0/node_modules/bar
    │       ├── baz   -> ../../baz@1.0.0/node_modules/baz

    ├── foo@1.0.0_bar@1.0.0+baz@1.1.0
    │   └── node_modules
    │       ├── foo
    │       ├── bar   -> ../../bar@1.0.0/node_modules/bar
    │       ├── baz   -> ../../baz@1.1.0/node_modules/baz
    ├── bar@1.0.0
    ├── baz@1.0.0
    ├── baz@1.1.0

附[V8.0.0]发布(github.com/pnpm/pnpm/r…)

  • auto-install-peers 默认是设置true

image.png

对比yarn、npm

我看版本都比较老旧了

[benchmarks-of-javascript-package-managers](github.com/pnpm/benchm…)

npm、yarn升级pnpm

1、 删除 node_modules

2、 在项目的package.json中添加以下代码,以防止其他人使用其他包管理器安装包

"scripts": { "preinstall": "npx only-allow pnpm",  }

3、在目录根目录下,创建一个名为pnpm-workspace.yaml的文件,单一存储库不需要

4、 pnpm i

5、 使用depcheck可以检测幽灵依赖