先上图:
左图:通过npm(yarn) 安装的项目依赖的 node_moudles 文件结构(不仅包含项目本身的依赖,也包含各个 package 自身的依赖,即子依赖)
右图:通过pnpm 安装的项目依赖 node_modules 的文件结构
可以明显看出,pnpm 的依赖更少(只有 package.json 中存在的 dependencies 和 devDependencies 才会被安装)
通过 npm2.x 安装依赖的缺点
- 包路径太长,在 Windows 系统下容易造成长路径问题
- 一个 package 会有多个副本存在,造成多余的依赖,导致不必要的磁盘占用 (上图中的 foo 引用 bar,如果这时存在另一个文件 aaa 也引用了 bar package,那么这个 aaa 下的 node_modules 也会存在一个 bar package)
通过 npm3.x / yarn 安装依赖的问题
npm3.x 通过拉平(flattered)依赖关系解决了npm@2.x依赖多个副本的问题,node_modules 文件结构如下:
node_modules
-
package A
-
package B
-
package C
但依然存在以下问题:
- 包可以互相访问(因为 node_modules 结构被拉平了,包与包之间在同一层级结构下,那么一个 package 可以访问任何一个 package,即使这个包并没有引用另一个包),可能会造成安全问题(幽灵依赖)
- 因为这个 flat 算法的复杂度高,所以安装速度很慢
- 有一些包不得不在 node_modules 文件夹下,如果强行 flat,可能会有引用问题
- 不同包会依赖同一个包的不同版本,flat 后可能会报错
yarn 的处理基本和npm@3.x一致
pnpm 如何解决 npm 的问题
pnpm 会在磁盘上新建一个中心化的pnpm store来保存所有的依赖, 后面在项目中再次使用都会直接使用 hardlink(硬链接),将项目中的包 link 到 store,从而大大节省磁盘空间。
pnpm
首先将依赖安装到全局 store
,然后通过 symbolic link
和 hard link
来组织目录结构,将全局的依赖链接到项目中,将项目的直接依赖链接到 node_modules
的顶层,所有的依赖则平铺于 node_modules/.pnpm
目录下,实现了所有项目的依赖共享 store
的全局依赖,解决了幽灵依赖和 NPM 分身的问题。(如果项目引用了某个依赖的子依赖,pnpm 会报错,必须要在项目 package.json 里引入该 sub dependence,即用了 pnpm 能保证代码引入的包必须出现在 package.json 文件里)
hard link:
- 硬链接不会新建
inode
(索引节点),源文件与硬链接指向同一个索引节点 - 硬链接不支持目录,只支持文件级别,也不支持跨分区
- 删除源文件和所有硬链接之后,文件才真正被删除
symbolic link (软链接):
- 符号链接中存储的是源文件的路径,指向源文件,类似于
Windows
的快捷方式 - 符号链接支持目录与文件,它与源文件是不同的文件,
inode
值不一样,文件类型也不同,因此符号链接可以跨分区访问 - 删除源文件后,符号链接依然存在,但是无法通过它访问到源文件