npm2的依赖管理在安装npm2依赖时,比较简单明了,直接按照包依赖的树形结构下载并填充本地目录结构。比如项目中A和C都依赖B。不管依赖的B是不是同一个版本,都会直接无脑生成对应的树结构。比如我们现在有如下依赖:A@2.0.0:BaseA@1.0.0BaseB@2.0.0B@3.0.0:BaseA@1.0.0BaseB@2.0.1那么npm后在node_modules中生成的内容我会如下。这种结构很直观,但是有一个问题,如果项目依赖过多,可能会导致以下问题:生成的依赖嵌套很深同一个版本依赖大量冗余的npm3/yarn依赖管理npm3已经优化了npm2的情况,那么怎么优化呢?其实我们最直观的想法就是把树压扁,把依赖压扁,并不能解决深度嵌套和冗余依赖的问题。那么,在上面的例子中,如果我们使用npm3安装,最终生成的node_modules会有这样一个结构:是不是看起来好多了,但是这时候会出什么问题呢?让我们试试看。在项目中安装好A和B之后,可以看到我们项目本身的依赖文件中只有a_klx和b_klx,但是执行npmi命令后发现多了几个包a_base_klx和b_base_klx没介绍。其实这是a_klx和b_klx自己引入的一个npm包,只不过出现在我们的node_modules下。那么如果我们直接使用这两个包会怎么样呢?可以看到我们可以正常使用这两个我们没有在dependencies中声明的npm包,因为这两个包存在于我们项目的node_modules下。根据npm包的搜索规则,我们可以找到这两个包。所以这种依赖关系导致了以下两个问题:我们项目本身的node_modules结构不直观,依赖不安全。我们可以使用未在依赖文件中声明的npm包。其实第一点的问题不是很大,主要是第二点会导致一些奇怪的问题。还是拿我们前面的例子来说,在项目中直接使用a_base_klx就可以了,因为node_modules中这两个包其实是存在的,但不是永远存在的。万一有一天,a_klx和b_klx这两个基础包的引用被去掉了,a_base_klx和b_base_klx在node_modules中也就不复存在了,那我们的代码就会出问题了……那就是:我的代码什么都没有同时,我们很容易对这种处理方式产生疑问。如果我同时引用了同一个包的多个不同版本,会为我推荐哪个包,同时我每次npmi后推荐的包版本是否相同?会不会出现下一次2.0.0版本变成2.0.1版本的情况,比如我们下面的情况:A@1.0.0:BaseA@1.0.0BaseB@2.0.0B@1.0.0:BaseA@1.0.0BaseB@2.0.1D@1.0.0:BaseA@1.0.0BaseB@2.0.1生成的依赖如下:或者如下:其实好像后者更合理,因为有2这个包使用的是2.0.1版本,建议还是推荐一下吧。试试看:提到的最外层包是2.0.0版本,b_klx和d_klx的node_modules各有一个b_base_klx@2.0.1部分的内容我自己查了下。大部分的说法都是按照package.json中的先后顺序来决定谁被提议,前面的package依赖的内容先被提议。然而,我做了一些实践,发现并非如此。即使我把b_klx或d_klx移到最前面,建议的包还是a_klx依赖的2.0.0版本。然后查看了npm的源码,发现内部对获取到的依赖列表进行了一些处理,最终会通过localeCompare方法对依赖进行排序,所以会优先推荐字典序在前的npm包的底层依赖。对于我们的示例,它是a_klx所依赖的b_base_klx@。2.0.0将首先被提出。pnpm的依赖管理为了解决上述问题,pnpm采用了不同于npm/yarn的依赖管理方式。如果我们再次使用pnpm安装上述依赖,会发现项目的node_modules文件夹下只有当前package.json中声明的依赖(软链接),真正的模块文件存在于node_modules/。以名称@版本号的形式文件夹扁平化存储(解决重复安装依赖)。同时这样的设计也避免了之前无法访问非法npm包的问题,??因为当前项目的node_modules中只有我们声明过的依赖,这也使得node_modules中的文件看起来非常直观。同时,node_modules/.pnpm中存储的文件实际上是pnpm实际缓存文件的“硬链接”,从而避免了多个项目带来的多个相同文件带来的空间浪费。但是说到硬链接,还有一个问题,那就是所有的项目都依赖同一个文件,所以在一个项目中修改某个npm包文件会影响到其他项目,这对于postinstallfriendly来说是非常难受的。然后继续实践,确实在不同的项目中修改一个npm包会影响到其他项目。同时我一般直接在node_modules中调试一些东西。感觉这种处理方式对这种操作不是很友好。但从pnpm官网来看,其实它默认会使用copy-on-write的方式进行处理,即如果你尝试修改内容,会复制一个文件,而不会影响源文件。那么为什么不起作用好像是因为libuv的bug:https://github.com/pnpm/pnpm/issues/2761,所以当copy-on-write没有生效时,又回落到hardlink方法来处理。
