pnpm/npm/yarm 对比分析(为什么我们要选择pnpm?)

181 阅读4分钟

pnpm/npm/yarm究极对比分析

包管理工具最大的区别

toolversionsmannerdefect是否支持workspace
npm1、2嵌套式重复依赖
npm3+扁平化幽灵依赖、分身依赖
npm5+扁平化、look幽灵依赖、分身依赖
npm7+扁平化、look幽灵依赖、分身依赖
yarmx扁平化幽灵依赖、分身依赖
pnpmxstore、hard link、symbolic link兼容性问题

tip-1:npm v7、yarm和pnpm管理工具都可以实现workspace(既实现monorope式管理项目管理)  
tip-2:幽灵依赖:某个包没有被安装,但用户可以引用(package.json 中并没有,但是用户却可以引用到这个包)。  
tip-2:分身依赖:分身依赖表示同一依赖,不同版本 
tip-3:hard link(硬链接):链接与源文件同时指向一个物理地址(store),它与源文件共享存储数据,它俩拥有相同的 ​​inode​​ 
tip-4:symbolic link(软链接):软链接可理解为指向源文件的指针,它是单独的一个文件,仅仅只有几个字节,它拥有独立的 ​​inode​​(相当于电脑上的快捷方式)

现在来依次介绍下npm和yarm包管理工具的问题以及pnpm怎么解决这些问题

npm 1/2 :在npm1/2版本中node_modules文件夹会呈现嵌套式的分布,其中foo1和foo2都引入了bar,但是这样的包管理方式会导致bar多次被引入,会让整个包体积变大如下:

node_modules
├─ foo1
│  ├─ index.js
│  ├─ package.json
│  └─ node_modules
│      └─ bar
│          ├─ index.js
│          └─ package.json
└─ foo2
   ├─ index.js
   ├─ package.json
   └─ node_modules
       └─ bar
           ├─ index.js
           └─ package.json

问题1:为什么会出现重复依赖? 
问题2:重复依赖会导致什么问题?

npm 3+/yarm:在npm 3+/yarm版本中node_modules文件夹会呈现扁平化分布,因为nod_model中的所有依赖包都会被摊平,这样相当于 foo 和 bar 出现在同一层级下面。那么用户就能在项目中require到foo1/foo2(这符合逻辑),但是同样也能直接require到bar(这不符合逻辑),这时bar 就成了一个幽灵依赖。

这样的幽灵依赖就会导致:

1.兼容性:因为我们的package.js文件中没有声明依赖bar的版本,因此这个bar版本不会被锁住,当其他开发人员用的node版本不统一,或者是用的打包工具不统一的时候,去运行项目,可能会下到到与当前依赖不兼容的版本。 
2.依赖缺失:当foo1/2跟新后,如果没有再用到bar依赖了,或者是用户没有安装devDependencies(开发环境),这样我们运行的时候就会因为找不到bar依赖而报错。

node_modules
├─ bar
│  ├─ index.js
│  └─ package.json
├─ foo1
│  ├─ index.js
│  └─ package.json
└─ foo2
   ├─ index.js
   └─ package.json
// package.js
{
    "name": "wsy",
    "version: "1.0.0",
    "main": "lib/index.js",
    "dependencies": {
        "foo1": "1.0.0",
        "foo2": "2.0.0"
    }
}

这样摊平依赖不仅会出现幽灵依赖还会出现分身依赖重复的问题,如果有多个包引入了不同版本的依赖,就会导致只有最先被引用的依赖会被摊平,其余的依赖还是会出现重复引入的情况,如下:

node_modules
├─ lodash@4.5.0
│  ├─ index.js
│  └─ package.json
├─ foo1
│  ├─ index.js
│  ├─ package.json
│  └─ node_modules
│      └─ lodash@3.6.0
│          ├─ index.js
│          └─ package.json
├─ foo2
│  ├─ index.js
│  ├─ package.json
│  └─ node_modules
│      └─ lodash@3.6.0
│          ├─ index.js
│          └─ package.json
└─ bar
   ├─ index.js
   └─ package.json

问题1:为什么会出现幽灵依赖?
问题2:幽灵依赖会导致什么问题?
问题3:为什么会出现分身依赖?
问题4:分身依赖会导致什么问题?

pnpm管理工具

pnpm:pnpm使用了本地储存store,安装时会在本地自动创建一个store文件夹,将依赖包下载到store文件夹中,然后通过hard link(硬链接)链接到node_model文件中,再通过symbolic link(软链接)链接到需要这些依赖的包中,如下:

1

问题1:为什么不全部使用硬链接?  答:因为node_modules中需要平铺的结构,避免引起深层嵌套。如果全部使用硬链接,那不就跟npm 1/2版本一样了。 
问题2:为什么不全部使用软链接?  答:实际上存在 store 目录里面的依赖也是可以通过软连接去找到的,nodejs 本身有提供一个叫做 --preserve-symlinks 的参数来支持 symlink,但实际上这个参数实际上对于 symlink 的支持并不好导致作者放弃了该方案从而采用 hard links 的方式。(在磁盘里兼容性不好) 
问题3:为什么没有幽灵依赖?  答:因为被打平的依赖会被放到 .pnpm 这个虚拟磁盘目录下面去,用户通过 require 是根本找不到的。 
问题4:为什么没有重复依赖 答:依赖文件都被放到计算机本地的store目录中,而我们在node_model中引用都是用软链接引用的的,软连接又相当于快捷方式,所以就算有很多软链接引用同一个依赖,但是实际上的依赖都是只有一个没有重复下载的。

参考资料:

# 浅谈pnpm & (软链接 与 硬链接) # pnpm 原理解析