# npm, yarn, 遇到了什么问腿?
为了得到这个问题的答案,我们需要对 npm,yarn 执行 npm install
or yarn install
后,在 node_modules
文件夹里面是怎么管理依赖的。
# npm3 版本之前对依赖的管理
npm3 版本之前在生成的 node_modules
文件夹对依赖的管理是嵌套结构的
假设我们有一个项目,它依赖于 b
包, c
包, b
包又依赖于 d
包和 f
包, c
包又依赖于 d
包和 f
包
依赖关系如下:
当我们运行 npm install
的时候,生成的 node_modules
文件夹中对依赖的管理会是嵌套结构的,如下:
node_modules
├── b
| └── node_modules
| └── d
| └── f
├── c
| └── node_modules
| └── d
| └── e
我们可以发现,是和依赖关系对应的嵌套结构
# npm3 版本之前对依赖的管理方式:嵌套式的 node_modules 文件结构的缺陷
- 嵌套的可能会非常深,就像
d
又依赖于d1
,d1
又依赖于d2
,d2
又依赖于d3
, 如此下去,嵌套的就会非常深,有的操作系统可能就难以支持了 - 同一个项目里会出现依赖重复安装,我们可以看到 d 包是被安装了两次的,在 b 包的
node_modules
里被安装了一次,在c
包的node_nodules
又被重复安装了一次 - 不同的项目里都依赖同一个依赖的时候,这个依赖在磁盘里会被重复安装。比如
x
项目和y
项目都依赖于z
包,那么z
包就会在磁盘里被安装两次
# npm3 版本之后和 yarn 对依赖的管理
npm3 以后的版本和 yarn 生成的 node_modules
文件夹是扁平结构的
根据我们上面的项目例子,它的 node_modules
文件夹结构应该是如下的:
node_modules
├── b
├── c
├── d
├── f
└── e
我们可以发现所有的依赖都被拍平了,是扁平化的,都被提升到了 node_modules 文件夹下面,而不是嵌套的。
# npm3 版本之后和 yarn 对依赖的管理方式:扁平式的 node_modules 文件结构 解决了之前的哪些问题
- 解决了嵌套的非常深的问题
采用了扁平式的结构,完全不存在嵌套 - 解决了同一个项目里依赖重复安装的问题
我们可以看到d包
在node_modules
文件夹下面是只安装了一次的。
那么b包
orc包
包要怎么找到他们依赖的d
包呢,因为他们自己目录下没有node_modules
,就会到上层目录里找node_modules
,就可以找到项目根目录下面的node_modules
,里面就有他们需要的d
包
ps:依赖的查找方式是先在自己包目录下的 node_modules 目录下面找,如果不存在 or 没找到,就到上层目录的 node_modules 目录找,以此方式,不断的往上找
# npm3 版本之后 和 yarn 对依赖的管理方式:扁平式的 node_modules 文件结构 带来了哪些新的问题,还存在哪些问题
- 带来的新的问题
- 带了了
幽灵依赖的问题
- 什么是幽灵依赖
在我们上面的例子里,我们项目a
只依赖于b
和c
,也就是说package.json
里的dependencies
只声明了b
和c
,但是因为扁平化的结构,我们可以在项目里使用package.json
的dependencies
里没有声明的d
,e
,f
。d
,e
,f
这三个依赖没有在项目目录的package.json
里声明,但是却可以使用,这种依赖就被称为幽灵依赖
幽灵依赖
会造成什么后果d
是被b
依赖的,然后因为扁平化
的结构,我们才能使用,那么如果有一天b
不在依赖于d
了,那么我们一旦npm install
,node_modules
里就不会再有d
了,而我们的项目代码还在使用d
,那么马上就会报错
- 什么是幽灵依赖
- 带了了
- 之前存在但是没有得到解决的问题
- 不同的项目里都依赖同一个依赖的时候,这个依赖在磁盘里会被重复安装。
# 为什么选择 pnpm(pnpm 的优势)
# pnpm 对依赖的管理
pnpm 对依赖的管理是一种扁平式和嵌套式相结合的,利用了软连接和硬链接的一种结构
ps: 这里简单的讲解一下什么是软连接,什么是硬链接
- 硬链接
在操作系统的文件系统里,磁盘的文件都会有一个编号叫索引节点号 (Inode Index), 而硬链接就是文件名直接指向这个索引号,从而找到磁盘里的文件内容。并且可以存在多个文件硬链接同一个索引节点号。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。 - 软连接
软连接也叫符号链接,它是一个保存有其他文件位置信息的文件,指向的是其他文件的位置信息,而不是磁盘里的文件的索引节点号。所以一旦它指向的那个文件被删除,它就找不到了 - 详细的讲解请看这篇文章
Linux 软连接和硬链接
依旧是我们上面的例子,它的 node_modules
文件夹结构应该是如下的:
node_modules
├── .pnpm
└── b // 硬连接 指向的是磁盘里的b包
└── node_modules
└── d // 软连接 指向的是 .pnpm/d
└── f // 软连接 指向的是 .pnpm/f
└── c
└── node_modules
└── d // 软连接 指向的是 .pnpm/d
└── e // 软连接 指向的是 .pnpm/e
└── d // 硬连接 指向的是磁盘里的d包
└── e // 硬连接 指向的是磁盘里的e包
└── f // 硬连接 指向的是磁盘里的f包
└── node_modules
└── b // 软连接 指向的是.pnpm/b
└── c // 软连接 指向的是.pnpm/c
└── d // 软连接 指向的是.pnpm/d
└── e // 软连接 指向的是.pnpm/e
└── f // 软连接 指向的是.pnpm/f
├── b // 软连接 指向的是 .pnpm/b
├── c // 软连接 指向的是 .pnpm/c
看到这个文件结构的时候第一感觉肯定是,好复杂啊,一下子多了好多东西,没关系,我接下来会逐一介绍为什么会是这样的,然后你就会发现这个结构真的是十分巧妙
node_modules
我们会发现
node_modules
目录下的包文件和我们项目里的package.json
里dependencies
声明的包是一摸一样的。这样就可以有效的避免幽灵依赖的问题
,项目里就不能直接使用d
,e
,f
这些幽灵依赖了,因为node_modules
里面没有。完美解决幽灵依赖的问题好,聪明的你马上就会问,
b
,c
都有各自的依赖呀,可是node_modules/b
,node_modules/c
里面没有node_modules
呀,那怎么找到他们的依赖呢。那么现在就告诉你真相,node_modules/
下面的包文件全部都是软连接
,他们都指向node_modules/.pnpm/
目录下 对应的包,也就是node_modules/.pnpm/b
,node_modules/.pnpm/c
, 他们下面就有对应的node_modules
来存储他们各自对应的依赖啦。
node_modules/.pnpm
- 我们会发现
node_modules/.pnpm
目录下拥有着我们项目所有的依赖,b
,c
,d
,e
,f
,并且是扁平化的,而且他们都是硬链接
。都指向磁盘里的b
,c
,d
,e
,f
。这样当我们在其他项目里面也有这些依赖的时候,就不需要在磁盘里面重复安装,直接一个硬连接
指向磁盘里对应的文件就可以。完美解决不同项目里同样的依赖在磁盘里重复安装的问题
- 我们会发现
node_modules/.pnpm/ 包文件 /node_modules
- 我们会发现
node_modules/.pnpm/包文件/node_modules/
目录下拥有者包所需要的依赖, 而这些依赖其实都是软连接,指向node_modules/.pnpm
目录下对应的包文件。在这里就是node_modules/.pnpm/b/
下面有着d
,f
, 但其实这个d
,f
都是软连接,指向node_modules/.pnpm/d
,node_modules/.pnpm/f
。这样虽然b
,c
都依赖于d
, 但是却不会重复安装,而是都指向node_modules/.pnpm/d
。 完美解决同一项目里依赖重复安装的问题
- 我们会发现
node_modules/.pnpm/node_modules
- 我们会发现
node_modules/.pnpm/node_modules
这个目录下存有项目的所有依赖。当然,都是软连接,指向node_modules/.pnpm
下面的包,那么这个文件夹到底是什么作用呢。 - 我们都知道
幽灵依赖
是不安全的,是很容易导致问题的,但是现实是,依旧很多第三方包使用了幽灵依赖,而我们的 pnpm 也对幽灵依赖做了兼容,在特定的情况下是允许幽灵依赖的,那么幽灵依赖在哪找呢,就是这个目录下面,这个目录是扁平化的,拥有项目所有的包。 实现了对幽灵依赖的支持 但是最好不要这样 因为幽灵依赖并不安全
- 我们会发现
# pnpm 的优势,解决了哪些问题
- 通过软连接避免了
幽灵依赖
的问题 - 通过硬连接解决了
不同项目的相同依赖
在磁盘重复安装的问题,提升了速度 - 通过软连接,
node_modules/.pnpm
的扁平化
结构解决了同一项目里相同依赖
重复安装的问题 - 通过软连接和
node_modules/.pnpm/node_modules
目录,兼容了幽灵依赖