浅谈前端包管理工具

260 阅读4分钟

pnpm 没出现之前,大家最常用的包管理工具有两个 npm、yarn,那为什么会出现 pnpm 呢,以及 pnpm 的出现到底解决了哪些问题?

npm

1、npmnode_modules 采用了嵌套结构,包的依赖被直接下载在当前目录下,形成依赖地狱,最关键的是windows上支持的文件路径最长是 260 多个字符。

2、同一个版本的包文件,有可能被不同的包引入,造成重复下载,从而浪费了大量的磁盘空间。

npm2.x 目录结构

node_modules
    ├── moduleA@1.0.0
    │   └── node_modules
    │       └── moduleC@1.0.0
    └── moduleB@1.0.0
        └── node_modules
            └── moduleC@1.0.0
                └── node_modules
                    └── moduleD@1.0.0

从上面的目录结构我们能看到,多层的嵌套结构,以及 moduleC@1.0.0 被重复下载了两次。

npm 官方也很快发现了这个问题, 并在 npm3.x 中做了改进。

npm3.xnode_modules 采用了扁平化的结构。将所有的子依赖都提升到跟主依赖平级的目录下。也就是 hoist 机制。

npm3.x 目录结构

── node_modules
   ├── moduleA
   ├── moduleB
   └── moduleC

这在很大程度上解决了地狱依赖的问题,以及磁盘空间的浪费问题,但同时又带来了其他问题

1、幽灵依赖
在package.json中没有声明依赖的包,因为其他包依赖,被下载在 node_modules 根目录下,导致也可以直接在项目中直接引用

2、下载文件的不确定性
同一份 package.json,下载出来的 node_modules 可能不一致的问题。

也就是说两个依赖包同时依赖的C文件的不同版本,但只有一个版本才能被 hoist,那么到底应该提升哪个版本呢,答案是看安装时的顺序。

npm 5.x 为此新增了 package-lock.json 文件,强制锁定依赖文件的版本号,以及不同版本如何 hoist 的问题。

这样就确保了同一份 package.json 下载出来的 node_modules 目录结构一致、但 幽灵依赖 问题仍然没有得到解决

yarn

yarn 的出现,弥补了 npm2.x 的众多缺陷。

1、采用扁平化的方式管理 node_modules,并且首次提出了下载缓存的概念,下载速度大幅提升。

2、yarn.lock
yarn.lock 文件记录了包文件的版本号,以及它们之间的依赖关系,确保同一个项目生成同样的 node_modules

但是 yarnnpm 一样,都没有解决幽灵依赖的问题。

pnpm

pnpm 在 2017 年发布,它的出现,解决了 npm、yarn 长期以来存在的性能、安全访问等问题。

我们来看看官方的介绍:

Fast, disk space efficient package manager

译为:一个快速的,高效的磁盘空间利用的包管理器。

我们执行下 pnpm install,看下输出内容

Packages are hard linked from the content-addressable store to the virtual store
  Content-addressable store is at: {home dir}/Library/pnpm/store/v3
  Virtual store is at:             node_modules/.pnpm

Content-addressable store(CAS),是一种存储信息的方式,根据内容进行检索信息的存储方式。

Virtual store(虚拟存储),指向存储的链接的目录,所有直接和间接依赖项都链接到此目录中,项目当中的.pnpm目录

pnpm install完成后,我们再看下 node_modules 的目录结构

node_modules
 .pnpm
   <package-name>@<version>/node_modules/<package-name>
 react
 eslint
 ...

我们可以发现,只有项目中真正依赖的包,才会软链在node_modules的目录下,其他所有的子依赖,以<package-name>@<version>的方式平铺在.pnpm目录下,因为没有依赖提升的概念,所以没有 幽灵依赖 这个问题

pnpm 是如何处理包之间的依赖关系呢? Store + Link

1、Store

pnpm 默认会将所有依赖包下载到系统{home dir}/Library/pnpm/store/v3中,系统中相同文件只会存在一份。

2、硬链接

pnpm install 的时候会先看Store中是不是存在该文件,如果存在,则会在.pnpm下新创建一个该文件的硬链接,hard linkStore 中对应的文件,而不是重新下载。下载速度、磁盘的利用率都得到大幅提升。

3、软链接

项目中依赖文件以及子依赖文件则是通过软链接的方式联系起来,所有的软链都是从 .pnpm <package-name>@<version>/node_modules/<package-name> 目录下链接过来

可以配合官方的图片具体看一下软硬链接的例子

image.png

总结

npm、yarn 存在的 幽灵依赖、下载速度慢、占用磁盘空间 等问题一直没有解决

pnpm 通过CAS的方式管理文件,通过软硬链接的方式来管理包与包之间的依赖。它的出现,解决了包管理器一直存在的痛点问题从而脱颖而出。