npm、yarn、pnpm的优劣与pnpm的原理与使用

一、前言

前端常见包管理工具:yarn、npm、pnpm,本文主要理解三种包管理工具的优劣与pnpm的原理与使用。

官网:

二、npm、yarn的介绍与对比

npm v1/ npm v2:嵌套结构

概述

虽然嵌套结构层次清晰,但会导致下载重复依赖包、文件引入路径长和未知bug

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 结构是可预测和干净的,因为 node_modules 中的每个依赖项都有自己的 node_modules 以及所包含依赖关系的 package.json 文件。

问题:

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

  • 依赖层级太深,导致文件路径过长。

  • 下载大量重复依赖:如上述node_modules目录,b包会在a包、c包两者的node_modules中安装,即重复安装。

npm v3/ yarn v1:扁平化结构

概述

扁平化结构解决了依赖重复下载、层级太深的问题,但会导致某项package.json没有注册的包,开发者也能引入

yarn的诞生

  • yarn是由Facebook、Google、Exponent 和 Tilde 联合推出了一个新的JS包管理工具;

  • yarn 是为了弥补早期npm的一些缺陷而出现的;

  • 早期的npm存在很多的缺陷,比如安装依赖速度很慢、版本依赖混乱等等一系列的问题;

  • 虽然从npm5版本开始,进行了很多的升级和改进,但是依然很多人喜欢使用yarn;

  • yarn生成的依赖关系文件为yarn.lock而npm为package-lock.json,内部格式也不相同

npm和yarn常见命令对比

扁平化结构图

 node_modules
├─ a
|  ├─ index.js
|  └─ package.json
└─ b
|  ├─ index.js
|  └─ package.json
└─ c
   ├─ index.js
   └─ package.json

优点:

  • 依赖目录平铺,解决层级太深问题

  • 减少重复依赖下载,相同依赖只提升某一版本(仍有大量重复依赖的下载)

问题:

  • Phatom dependencies(幽灵依赖):使用 npm 或 Yarn Classic 安装依赖项时,所有包都被提升到node_modules的根目录。 因此,项目可以访问到未被添加进当前项目的依赖。

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

  • 依赖结构的不确定:如上述a、c依赖的b包版本不同,那么b包哪个版本会被提升呢?

答案是:都有可能。取决于 a 和 c 在 package.json中的位置,如果 c声明在前面,那么就是前面的结构(b@1.1.0被提升),否则是后面的结构(b@1.0.0被提升)。这就是为什么会产生依赖结构的不确定问题,也是 lock 文件诞生的原因,无论是package-lock.json(npm 5.x才出现)还是yarn.lock,都是为了保证 install 之后都产生确定的node_modules结构。尽管如此,npm/yarn 本身还是存在扁平化算法复杂和package 非法访问的问题,影响性能和安全。

三、pnpm介绍

pnpm:我们可以理解成是performant npm缩写;

与npm和yarn对比

  • 快速:pnpm 比其他包管理器快2倍

  • 高效:node_modules中的文件链接自特定的内容寻址存储库

  • 支持monorepos:pnpm内置支持单仓多包

  • 严格:pnpm默认创建了一个非平铺的node_modules,因此代码无法访问任意包

设计初衷

1、节约磁盘空间并提升安装速度

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

  • 如果你用到了某依赖项的不同版本,只会将不同版本间有差异的文件添加到仓库。 例如,如果某个包有100个文件,而它的新版本只改变了其中1个文件。那么 pnpm update 时只会向存储中心额外添加1个新文件,而不会因为仅仅一个文件的改变复制整新版本包的内容。

  • 所有文件都会存储在硬盘上的某一位置。 当软件包被被安装时,包里的文件会硬链接到这一位置,而不会占用额外的磁盘空间。 这允许你跨项目地共享同一版本的依赖。

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

使用 npm 安装某个项目时这个项目可能会依赖其他项目

2、创建非扁平化的 node_modules 文件夹

使用 npm 或 Yarn Classic 安装依赖项时,所有包都被提升到node_modules的根目录。 因此,项目可以访问到未被添加进当前项目的依赖。

默认情况下,pnpm 使用软链的方式将项目的直接依赖添加进模块文件夹的根目录。

四、pnpm原理

概述

pnpm使用符号链接和硬链接来构建node_modules目录,在这之前先让我们了解一下操作系统的相关知识,及文件系统、硬链接、符号(软)链接。

文件系统

扇区(sector)

硬盘的最小存储单位叫「扇区」(sector,每个扇区存储512bit)

操作系统一次读一个扇区效率太低,所以会一次读多个扇区

块(block)

8个扇区组成一个块,最常见的大小为4kb

块是文件存取的最小单元,文件数据都存储在块中

索引节点(inode)

  • 存储文件元信息,比如文件的创建者、文件的创建日期、文件大小等

  • Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

  • 打开文件时,系统首先找到文件的inode号码,然后通过inode号码获取inode信息。然后根据inode中的文件数据所在block读取数据。

在Linux系统中,内核为每一个新创建的文件分配一个inode,每个文件都有一个惟一的inode号,我们可以将inode简单理解成一个指针,它永远指向本文件的具体存储位置。文件属性保存在inode里,在访问文件时,inode被复制到内存,从而实现文件的快速访问,系统是通过inode来定位每一个文件。

简述文件储存

在操作系统中,文件实际上是一个指针,只不过它指向的不是内存地址,而是一个外部存储地址(这里的外部存储可以是硬盘、U盘等)

新建文件

新建helloWord.js文件并添加数据:实际上我们只是将该文件的指针指向某片扇区,我们对文件操作时也只是修改该扇区中的数据。

删除文件

当我们删除helloWord.js时,实际上我们只是将指针删除了,因此我们发现文件无论多大我们都可以快速删除,而清空废纸篓时就很慢。其实我们的U盘、硬盘里的文件虽然说看起来已经删除了,但是其实数据恢复公司是可以恢复的,因为数据还是存在的,只要删除文件后再没有存储其它文件就可以恢复,所以真正删除一个文件就是疯狂存疯狂删。

复制文件

我们通常的 复制粘贴 是将该文件指针指向的内容进行复制,然后开辟一片新的扇区进行储存,而不是指向同一片扇区,使用我们修改当前文件不会影响其他文件,我们也可以通过硬链接来实现这一操作,详细看下文。

硬链接与符合(软)链接

硬链接(hard link)

概述

硬链接实际上是一个指针,指向源文件的inode,系统并不为它重新分配inode。

硬链接不会建产新的inode,硬链接不管有多少个,都指向的是同一个inode节点,只是新建一个hard link会把结点链接数增加,只要结点的链接数不是0,文件就一直存在,不管你删除的是源文件还是链接的文件。只要有一个存在,文件就存在(其实就是引用计数的概念)。

当你修改源文件的时候,其他的文件都会做同步的修改。

硬链接的概念来自于 Unix 操作系统,它是指将一个文件A指针复制到另一个文件B指针中,文件B就是文件A的硬链接。

通过硬链接,不会产生额外的磁盘占用,并且两个文件都能找到相同的磁盘内容。硬链接的数量没有限制,可以为同一个文件产生多个硬链接。

硬链接文件有两个限制

  • 不允许给目录(文件夹)创建硬链接

  • 只允许在同一文件系统中的文件之间才能创建链接

符号(软)链接

概述

符号链接最直观的解释:相当于Windows系统的快捷方式,是一个独立文件(拥有独立的inode,与源文件inode无关),实际上是特殊文件的一种, 该文件的内容是源文件的路径指针,另一个文件的位置信息,通过该链接可以访问到源文件。

所以删除软链接文件对源文件无影响,但是删除源文件,软链接文件就会找不到要指向的文件(可以类比Windows上快捷方式,你点击快捷方式可以访问某个文件,但是删除快捷方式,对源文件无任何影响)。

硬链接和符号链接的小结

硬链接:
  • 硬链接仅能链接文件,而符号链接可以链接目录;

  • 具有相同inode节点号的多个文件互为硬链接文件;

  • 硬链接不占用磁盘空间;

  • 删除硬链接文件或者删除源文件任意之一,文件实体并未被删除;

  • 只有删除了源文件和所有对应的硬链接文件,文件实体才会被删除;

  • 硬链接文件是文件的另一个入口;

  • 可以通过给文件设置硬链接文件来防止重要文件被误删;

  • 创建硬链接命令 ln 源文件名 生成硬链接文件名(大家可以通过该命令自己测试软硬链接);

  • 硬链接文件是普通文件,可以用rm删除;

  • 对于静态文件(没有进程正在调用),当硬链接数为0时文件就被删除。注意:如果有进程正在调用,则无法删除或者即使文件名被删除但空间不会释放。

符号链接/软链接:
  • 软链接类似windows系统的快捷方式;

  • 软链接里面存放的是源文件的路径,指向源文件;

  • 软链接占用的空间只是存储路径所占用的极小空间;

  • 删除源文件,软链接依然存在,但无法访问源文件内容;

  • 软链接失效时一般是白字红底闪烁;

  • 创建软链接命令 ln -s 源文件名 生成软链接文件名;

  • 软链接和源文件是不同的文件,文件类型也不同,inode号也不同;

  • 软链接的文件类型是“l”,可以用rm删除。

  • 由于符号链接指向的是另一个文件或目录,当node执行符号链接下的JS文件时,会使用原始路径。比方说:我在E盘装了CFHD,在桌面创建了CFHD快捷方式,相当于是符号链接,双击快捷方式运行游戏,在运行游戏的时候是按照CFHD原始路径(E盘路径)运行的。

快捷方式

快捷方式类似于符号链接,是windows系统早期就支持的链接方式。它不仅仅是一个指向其他文件或目录的指针,其中还包含了各种信息:如权限、兼容性启动方式等其他各种属性,由于快捷方式是windows系统独有的,在跨平台的应用中一般不会使用。

node环境对硬链接和符号链接的处理

硬链接:

硬链接是一个实实在在的文件,node不对其做任何特殊处理,也无法区别对待,实际上,node根本无从知晓该文件是不是一个硬链接。

符号链接:

由于符号链接指向的是另一个文件或目录,当node执行符号链接下的JS文件时,会使用原始路径。比方说:我在E盘装了CFHD,在桌面创建了CFHD快捷方式,相当于是符号链接,双击快捷方式运行游戏,在运行游戏的时候是按照CFHD原始路径(E盘路径)运行的。

npm与pnpm按照依赖对比

我们分别执行 npm i axios 与 pnpm add axios 对比

npm i axios

可以看出axios与axios习惯依赖全部都是放在同一级的,使用就会出现上文中描述的幽灵依赖。

pnpm add axios

可以看到node_modules变得清晰了很多,只有我们想要安装的目标依赖vue和.pnpm文件夹,这就很好的杜绝了上文中描述的幽灵依赖,我们还可以看到。

axios相关的依赖和依赖的依赖都被放在了.pnpm 中

我们打开pnpm的node_modules所在目录可以看见左下角有个小箭头,包括上张图片axios文件最后也有小箭头,

我们打开axios的详情可以看到他只是 .pnpm/axios的替身而已,及也就是axios的软链接

我们点开pnpm/node_modules/.pnpm/axios可以看见axios相关的依赖,巧妙的是相关依赖也都是软链接,链接到.pnpm下对应的文件

而 .pnpm 下一级的文件都是没有小箭头的,.pnpm是虚拟存储目录,这一级的文件是从pnpm全局的store硬链接生成的文件,在mac中该文件夹通常在User/username/Library/pnpm/store或User/username/.pnpm-store中

通过上面的图文描述我们再来看pnpm的 官网首页 => 创建非扁平化的 node_modules 文件夹 章节下的这个图片我相信大家都可以看懂

五、pnpm常用命令

以下为常用命令更多请看官网 CLl命令 章节

// 用于安装项目所有packagepnpm install // 默认会从 npm registry 安装最新的 package-name。pnpm add package-name // 删除某个package 别名: rm, uninstall, un 添加 -g 从全局删除一个packagepnpm remove package-name // 移除不需要的packages,及没有项目引用的(不建议经常使用,因为依赖越多按照速度可能越快)pnpm prune

六、删除所有node_modules和npm、yarn的package-lock配置文件

请看该章节:删除所有node_modules和npm、yarn的package-lock配置文件

七、总结pnpm

  • 主要解决多个项目可能按照重复package,导致占有磁盘空间

  • 解决项目可以访问没有安装的依赖,及安装某些package时该package依赖的package我们没有主动按照也可以使用

  • 创建非扁平化的node_modules

  • 通过软硬链接结合实现更快的安装速度

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值