pnpm、yarn、npm的区别

发布时间 2023-09-01 10:36:54作者: 卡卡Kk

pnpm、yarn、npm的区别

一、npm

npm的全称Node Package Manager,也就是Node包管理器。它现在不仅仅作为包管理工具,在前端项目中我们经常使用npm来管理我们的项目依赖。

1.如何下载npm?

npm属于node的一个管理工具,所以只要安装了node,npm就自动被安装了。安装Node的过程会自动安装npm工具; npm的官网:www.npmjs.org/ 可以通过官网查看npm的一系列知识。

2.npm存放的包放在哪里?
npm的包实际上是存放在registry里面中的,我们自己发布的包也会保存到registry当中,当我们通过npm安装某个包的时候,其实npm是从registry中下载的。

3.npm版本的缺陷
可以看到每个依赖版本前面都有个"^"符号,有必要解释下这个版本的概念。
当x.y.z的情况下:表示是一个明确的版本号
当^x.y.z的情况下:表示是x版本保持不变,y和z永远是最新的。
当~x.y.z的情况下:表示的是x和y是保持不变的,z永远是最新的。

如图所示,css-loader:^6.7.1,这个字符告诉npm,安装主版本等于6的任意一个版本即可。所以如果我现在运行npm进行安装,npm将安装css-loader的主版本为6的最新版,可能是css-loader@6.8.5

理论上新版本是向下兼容的,安装最新版是不会有什么问题的,应该能正常运行。

但是,另一方面,即使不同的开发人员使用了相同的package.json文件,在他们自己的机器上也可能会安装同一个库的不同种版本,这样就会存在潜在的难以调试的错误和“在我的电脑上…”的情形。
因此npm中的package.json中的依赖无法保持版本一致的缺陷,并且npm也依赖很多的库,这会导致嵌套依赖关系,并增加无法匹配相应版本的几率。

4.anpm依赖结构的缺陷
在npm3以前,假如有一个项目依赖了A模块,A模块又依赖了B模块、B模块又依赖了C模块,那么他们所构成的依赖树结构如下图所示:

这个结构会很长很长,这对于基于Unix的操作系统来说只不过是一个小烦恼,但对于“windows”来说却是个破坏性的东西,因为有很多程序无法处理超过260个字符的文件路径名。

而npm3以后意识到该问题,npm则采取扁平化的方式的依赖树的方式去解决这个问题,如图所示

这样一个从原来很长的"nodeModules/moduesA/moudesB/moduesC"为"nodeModules/modulesC",这种方法的缺点是:npm必须首先遍历所有的项目依赖关系,然后再决定如何生成扁平的node_modules目录结构。

npm必须为所有使用到的模块构建一个完整的依赖关系树,这是一个耗时的操作,因此npm安装依赖往往是很慢的。

二、yarn
任何一个新技术的出现,都是为了解决上个技术存在的痛点。
yarn是一种新的hadoop资源管理器,是一个通用资源管理系统,可为上层应用系统提供统一的资源管理和调度。

1.yarn版本的问题
yarn 解决了npm版本不确定性问题,yarn默认有一个 yarn.lock 文件锁定版本,它能保证"package.json"依赖安装的版本和实际的版本是一致,保持环境统一,不会出现像npm一样出现版本混乱的问题。

2.yarn安装依赖
yarn的安装依赖是异步的,例如同时安装axios和elementui,yarn不会阻塞下载,会同时下载axios和elementui,因此不会因为某个依赖安装太费时间而导致后面的依赖一直处于等待下载的状态。

3.yarn离线缓存
当yarn安装重复的依赖时,yarn会从本地获取,但是yarn提供了离线模式,yarn会从缓存中下载依赖,而npm虽然是本地获取,但它依旧会从网络下载。

4.缺点

对于少数的一些依赖包,可能会出现兼容性问题。在某些情况下,与npm不兼容。

arn 依然和 npm 一样是扁平化的 node_modules 结构,没有解决幽灵依赖依赖分身问题。

幽灵依赖:指在 package.json 中未定义的依赖,但项目中依然可以正确地被引用到,这会存在潜在的问题

三、pnpm
pnpm全称:performant npm ,意味“高性能的npm”。pnpm由npm/yarn衍生而来,解决了npm/yarn内部潜在的bug,极大的优化了性能,扩展了使用场景。被誉为“最先进的包管理工具”

pnpm运行起来非常的快,为什么这么快呢? 因为它采用了一种巧妙的方法,利用硬链接和软链接来避免复制所有本地缓存源文件,这是yarn的最大的性能弱点之一。

1.硬链接和软连接
硬链接(hard link)节约磁盘空间

在Linux的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号,在 Linux 中,允许多个文件名指向同一索引节点,一般这种连接就是硬链接。
硬链接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬链接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的链接。只删除一个链接并不影响索引节点本身和其它的链接,只有当最后一个链接被删除后,文件的数据块及目录的链接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬链接文件均被删除。

  • 硬链接以理解为源文件的副本,使得用户可以通过不同的路径引用方式去找到某个文件,他和源文件一样的大小但是事实上却不占任何空间。

符号链接(symbolic link)创建嵌套结构(软连接

准确来说叫符号链接(symbolic link),一般又叫软链接(soft link)。与硬链接共用一个索引节点不同,软链接会创建新的索引节点,并指向源文件。可以理解软链接就是windows系统中的桌面快捷方式。

  • 软链接可以理解为快捷方式,pnpm在引用依赖时通过符号链接去找到对应磁盘目录(.pnpm)下的依赖地址。非扁平化结构。

2.pnpm做了什么
当使用 npm 或 Yarn 时,如果你有100个项目使用了某个依赖(dependency),就会有100份该依赖的副本保存在硬盘上。而在使用 pnpm 时,依赖会被存储在内容可寻址的存储中,所以: 如果你用到了某依赖项的不同版本,那么只会将有差异的文件添加到仓库。 例如,如果某个包有100个文件,而它的新版本只改变了其中1个文件。那么 pnpm update 时只会向存储中心额外添加1个新文件,而不会因为仅仅一个文件的改变复制整个新版本包的内容。 所有文件都会存储在硬盘上的某一位置。 当软件包被被安装时,包里的文件会 硬链接 到这一位置,而不会占用额外的磁盘空间。 这允许你跨项目地共享同一版本的依赖。因此,您在磁盘上节省了大量空间,这与项目和依赖项的数量成正比,并且安装速度要快得多!

3.非扁平的目录
当使用 npm 或 Yarn 安装依赖包时,所有软件包都将被提升到 node_modules 的 根目录下。其结果是,源码可以访问 本不属于当前项目所设定的依赖包。
默认情况下,pnpm 则是通过使用符号链接的方式仅将项目的直接依赖项添加到 node_modules 的根目录下。

pnpm 使用.pnpm目录以平铺的形式储存着所有的包。
该目录通过@来实现相同模块不同版本之间隔离和复用,由于它只会根据项目中的依赖生成,并不存在提升,所以它不会存在之前提到的幽灵依赖(Phantom dependencies)问题!

然后使用 store + Links 和文件资源进行关联。简单说pnpm 把会包下载到一个公共目录,如果某个依赖在 sotre 目录中存在了话,那么就会直接从 store 目录里面去 hard-link,避免了二次安装带来的时间消耗,如果依赖在 store 目录里面不存在的话,就会去下载一次。

通过 store + hard link 的方式,不仅解决了项目中的 NPM分身(NPM doppelgangers)问题(当包有多个版本,会被重复安装多次),项目之间也不存在该问题,从而完美解决了npm3+ 和 yarn 中的包重复问题!