最近是不是经常听到pnpm,我也是。今天研究了一下它的机理,果然厉害。可以说是对yarn和npm的一次降维打击。这有什么好?一起来看看吧。先从npm2说起,根据包管理工具的发展历程:npm2使用node版本管理工具,将node版本降到4,那么npm版本就是2.x。然后找到一个目录,执行npminit-y,快速创建一个package.json。然后执行npminstallexpress,就会下载express包及其依赖:展开express,它还有node_modules:展开几层,每个依赖都有自己的node_modules:也就是说npm2的node_modules是嵌套的。这是正常的吗?怎么了?这其实是有问题的。多个包之间不可避免地会存在共同的依赖关系。如果这样嵌套,相同的依赖会被复制很多次,会占用比较大的磁盘空间。这不是最大的问题。致命问题是windows最长的文件路径超过260个字符,所以嵌套会超过windows路径的长度限制。当时npm还没解决,社区又想出了一个新的解决方案,就是yarn:yarnyarn是怎么解决重复依赖和嵌套路径长的问题的?铺平。所有的依赖不再一层层嵌套,而是都在同一层,这样就不存在重复依赖的问题,也不存在路径过长的问题。我们把node_modules删掉,用yarn重新安装,执行yarnaddexpress:此时node_modules是这样的:都铺在一层,下面的包大部分都不是二级node_modules:当然也有有些包里面还有node_modules,比如这个:为什么会有嵌套?因为一个包可能有多个版本,只能升级一个,所以后面遇到同一个包的不同版本时,还是采用嵌套的方式。npm升级到3之后,也采用了这种铺路方案,和yarn很像:当然yarn也实现了yarn.lock锁定依赖版本的功能,但是这个npm也实现了。yarn和npm都采用了paving方案,这个方案没有问题吗?不,扁平化解决方案也有相应的问题。最主要的问题是ghostdependencies,就是你明明没有在dependencies中声明的依赖,但是你可以在代码中require它们。这个也很好理解,因为都是铺好了,依赖的依赖也能找到。但是这种方式存在隐患,因为没有明确的依赖。如果有一天其他包不依赖这个包,你的代码将无法运行,因为你依赖这个包,但现在不会安装它。这就是Spectre依赖项的问题。而且还有一个问题,就是当上述依赖包有多个版本时,只会升级一个,其他版本的包还是要复制很多次,仍然存在浪费磁盘的问题空间。社区有解决这两个问题的想法吗?当然有,不就是pnpm出来了吗。那么pnpm是如何解决这两个问题的呢?回忆一下pnpm,为什么npm3和yarn需要扁平化node_modules?不就是因为同一个依赖会被复制多次,而且路径太长,所以在windows下有问题吗?如果您不复制它怎么办,例如通过链接。首先介绍一下链接,即软链接和硬链接。这是操作系统提供的一种机制。硬链接是对同一个文件的不同引用,而软链接则是新建一个文件,文件内容指向另一个路径。当然,这两个链接的使用是类似的。如果不拷贝文件,只把npm包的内容保存在全局仓库,其余地方链接起来怎么办?这样就不会因为多次拷贝而浪费磁盘空间,也不会有路径太长的问题。因为太长路径的限制本质上是不能有太深的目录层级,现在是链接到各个位置的目录,而不是同一个目录,所以没有长度限制。没错,pnpm就是通过这个思路实现的。再次删除node_modules,然后用pnpm重新安装,执行pnpminstall。你会发现它打印了这样一句话:Thepackageishardwiredfromtheglobalstoretothevirtualstore,这里的虚拟商店是node_modules/.pnpm。我们打开node_modules看看:确实不平,依赖express,所以node_modules下只有express,没有ghost依赖。展开.pnpm看看:所有的依赖都在这里铺好了,而且都是从全局store中硬链接出来的,然后通过软链接来组织包与包之间的依赖关系。比如expresss在.pnpm下,这些都是软链接,也就是说所有的依赖都是从全局store硬链接到node_modules/.pnpm,然后通过软链接相互依赖。官方给了一个示意图,大家一起看看就明白了:这就是pnpm的实现原理。那么回过头来看,为什么pnpm优秀呢?首先,最大的好处就是节省磁盘空间。全局只保存一份包,其余都是软硬连接。将节省多少磁盘空间。二是快,因为链接自然会快,而不是复制。这也是它标榜的优势:相对于npm2的优势在于它不会为同一个依赖创建多个副本。与yarn和npm3+相比,没有ghost依赖,不会出现复制多份未提升的依赖的问题。这已经足够好了,可以说是对yarn和npm的一次降维打击。总结最近经常听到pnpm,可以说是很火了。在这篇文章中,我们梳理了它爆掉的原因:npm2以嵌套的方式管理node_modules,会出现多次复制同一个依赖的问题。npm3+和yarn扁平化管理node_modules,解决了一些嵌套问题,但是引入了ghost依赖问题,同名包只会升级一个版本,其余还是会重复复制。pnpm使用了另一种方式,不再是复制,而是从全局store硬链接到node_modules/.pnpm,然后通过软链接组织依赖。这样不仅节省磁盘空间,而且没有ghost依赖问题,安装速度快,在机制上优于npm和yarn。这就是pnpm打击npm和yarn降维的方式。
