什么是幻影依赖
我们知道npm、yarn安装依赖包的时候是平铺的结构,如下图:
疑问:明明我package.json文件里只声明了express一个包,为啥有这么多的包被安装到node_modules目录中?
原因其实也都知道,是express这个包又依赖了其他的包,所以就一起安装进来了
那幻影依赖顾名思义就是没有在package.json文件里声明的依赖又出现在了node_modules目录中,这些依赖在生产版本中可能会造成影响
比如在文件中使用了lodash
这个库,但是没有在package.json
里声明这个依赖,这个依赖就是上述说的幻影依赖
一般会造成两个问题
一个是版本问题:
项目中声明了A库版本v1,A依赖于B库版本v1,这时候项目直接使用了B库(B库没有声明是幻影依赖);下一次升级A库到v2时候,依赖的B库也可能会升级到v2,这时候项目中使用的还是B库v1的版本,造成代码莫名其妙的报错,这种问题还不好排查,等到发生的时候再排查问题就大了
除了版本问题外,另外的一个问题是依赖丢失的问题:
如果A库是开发依赖,则导致在开发环境使用幻影依赖B库没有问题,但是一旦部署的时候就会导致生产环境没有安装开发依赖,使得B依赖丢失,则项目中使用的这个库找不到了,就会在生产环境上报错,那问题大了
解决方法
发现幻影依赖的本质原因后,解决方法也很简单
一种方法就是直接安装这个幻影依赖,这种做法直接有效,但是有个缺陷,就是有时候项目太大了,安装的依赖包太多了,开发人员不会一一去排查包是否是幻影依赖,会直接使用vscode中的智能提示,但是这种智能提示是根据在node_modules中是否有这个库去显示的,这时候就需要一种工具,提示在package.json中没有声明的依赖,你在代码中使用就要报错,马上提示这个库要安装
经常说的依赖关系树其实是一个依赖图结构,如上左侧所示
而把依赖安装过后,到文件系统中又变成文件树结构如上图中间所示,这种方式最早的时候就是npm用的文件方式这样会有重复文件
后来npm升级后以及yarn出现后就会把依赖安装成上图右边所示,扁平结构,这里就造成了幻影依赖了
使用pnpm
pnpm工具的出现完美的解决了上述问题,使用pnpm安装包后,会出现上图中间和右边所示的关系
首先会有个仓库形式的store,这里存储的是所有的安装包以及其依赖包,中间图所示的还是各个依赖之间的关系,不过是以符号链接
-即软连接的方式存储的,实际指向的是store中的文件地址
使用pnpm安装的包,在package.json里未声明的包在node_modules下是找不到的,这样在项目中引入就不会有提示,还会报错,上图右边的小箭头指的就是符号链接也就是软连接,真正的包地址是在./node_modules/.pnpm/express@4.18.2/node_modules/express
,express的依赖包又被软连接到.pnpm/node_modules目录下
硬连接:是指向文件或者目录的实际物理位置的直接引用,硬连接创建一个新的文件入口,但不创建新的文件内容,它与原始文件共享相同的inode(文件的唯一标识符)和数据块,因此,对硬连接的任何操作都会直接影响原始文件,删除原始文件或硬连接中的任何一个都不会导致数据丢失,只要至少还有一个硬连接存在
软连接(符号链接):是一种特殊类型的文件,包含了对另外一个文件或目录的引用,类似于快捷方式,软连接不直接指向文件的物理位置,而是通过文件路径名来引用目标文件或目录,当访问软连接时,操作系统会自动将其解析为目标文件或目录,删除软连接不会影响原始文件或目录的存在,但如果目标文件或目录被删除,软连接将变为无效
在pnpm中,默认情况下,使用软连接(符号链接)将项目的直接依赖添加到根目录下的node_modules文件夹下,而.pnpm目录中保存的直接和间接依赖的硬连接
下图是pnpm、npm、yarn的对比