npm,yarn,pnpm三者区别

69 阅读6分钟

npm

npm(Node Package Manager, 2010年)是用于 Node.js 的默认包管理器。它可以使开发人员轻松地共享和管理 JavaScript 包。npm 使用「package.json」文件来定义项目的依赖关系,并提供命令行工具来安装、更新和删除依赖项。

通过 npm 生成的 node_modules 结构通常是一个「扁平化的目录结构」,安装目录结构可能如下所示:

// node_modules 结构是扁平化的,但每个依赖项的包内部结构可能是嵌套的

// 假设你的项目依赖于包 A 和包 B
Project
└── node_modules
    ├── package-A
    └── package-B

// 如果多个依赖项依赖于同一个包的不同版本
Project
└── node_modules
    ├── package-A
    │   └── node_modules
    │       └── shared-package@1.0.0
    └── package-B
        └── node_modules
            └── shared-package@1.0.0

// 如果多个依赖项依赖于同一个包的相同版本
Project
└── node_modules
    ├── package-A
    │   └── node_modules
    │       └── shared-package@1.0.0
    └── package-B
        └── node_modules
            └── shared-package@1.0.0

这种「扁平化 的目录结构」存在的「缺点」🤕:

  • 冗余的依赖项:由于每个依赖项都被直接放置在 node_modules 目录下,如果多个依赖项依赖于同一个包的不同版本,那么这个包将会在node_modules目录下重复出现多次,造成了冗余的存储 🙁
  • 磁盘空间占用:由于每个依赖项都被直接放置在node_modules目录下,即使同一个依赖项版本相同的情况下,在不同的包中重复出现,也会占用额外的磁盘空间 🙁
  • 安装时间延长:由于扁平化结构中的所有依赖项都位于同一层级,安装时需要递归解析和安装所有依赖项。当项目具有大量依赖项或存在大量依赖项冲突时,按包顺序逐个执行的「串行安装」,会导致安装时间较长 🙁

Yarn

Yarn(2016年)是由 Facebook、Google、Expo 和 Tilde 等公司合作开发的 JavaScript 包管理器。它旨在改进 npm 的性能和可靠性,并引入了一些新功能,如并行安装、版本锁定和离线模式!🎉

下面是 Yarn 的一些优点和解决的问题:

  • 离线模式:Yarn 可以在离线环境下工作,它会缓存已经下载的依赖项,以便在没有网络连接时重新使用。这对于团队合作、CI/CD 构建和在低速或不稳定的网络环境中工作的开发人员来说特别有用。
  • 并行安装:Yarn 使用并行安装的方式,可以同时下载多个依赖项,从而提高安装速度。这对于具有大量依赖项或需要频繁重复安装的项目来说,可以显著减少等待时间。
  • 确定性安装:Yarn 确保在不同的环境中安装相同的依赖项版本,从而避免了由于依赖项版本的差异导致的构建或运行时错误。Yarn 使用 yarn.lock 文件来记录依赖项的确切版本,以确保跨团队和环境的一致性。
  • 优化的网络请求:Yarn 使用了更高效的网络请求算法,可以减少对网络的负载和响应时间。它能够智能地利用缓存和并行下载来最大限度地提高依赖项的获取速度。

这也是为什么当时 yarn 在安装速度上比 npm 有显著提高。需要注意的是,尽管 Yarn 带来了这些优点和改进,npm 也在后续的版本中逐渐改进自身,引入了类似的功能和优化。比如确定性安装这一点,npm 的 package-lock.json 文件就是在 npm v5.0.0 版本中引入的!🎉

pnpm

我们再来看看 pnpm 到底有什么特别之处,为什么 pnpm 是所谓的「快速的,节省磁盘空间的包管理工具」,它是怎么做到的?🤔

仓库-store

pnpm 有一个 store 的概念(类比redux的store),目录内部使用「基于内容寻址」的文件系统来存储磁盘上所有的文件。

基于内容寻址是一种文件系统的设计原则,以文件内容的哈希值作为文件的唯一标识符,并将文件存储在以哈希值命名的目录中。这样的设计使得相同内容的文件只会存储一次,避免了重复存储相同文件的问题。

在 pnpm 的 store 目录中,每个文件的名称都是其内容的哈希值,因此同一个文件无论在多个项目中使用,都只会被存储一次。当安装或更新依赖项时,pnpm 会检查 store 目录中是否已经存在所需的文件,如果存在则直接复用,避免了重复的下载和存储。

这种基于内容寻址的文件系统设计可以带来一些好处:

  • 磁盘空间的节省: 相同的文件只会存储一次,避免了冗余的存储,尤其是在多个项目共享依赖项时。
  • 快速的依赖项安装: 由于文件已经存在于 store 中,所以安装依赖项时可以直接使用已有的文件,而无需下载和解压,从而提高了安装速度。
  • 安全性: 基于内容寻址的文件系统使用哈希值来标识文件,这意味着文件内容的完整性可以通过哈希校验来验证,提供了一定的数据完整性保护。

pnpm install 过程中,我们会看到如下的信息:其中的 content-addressable store 就是以上提到的 store。Mac 中默认会将 store 设置到 {home dir}>/.pnpm-store/v3

image.png

软/硬连接-link

硬链接就是同一个文件的一个或多个文件名,它所引用的是文件的物理数据而不是文件在文件结构中的位置,用户可以通过不同的路径引用方式去找到某个文件。pnpm 会在全局的 store 目录里存储项目 node_modules 文件的硬连接。但是,硬链接只能用于文件不能用于目录(可能导致目录循环)。

硬链接的特点: 无论有多少个硬链接指向相同的数据块,它们都被视为相互独立的完整文件副本。因此,硬链接不会占用额外的磁盘空间,因为它们共享相同的数据块。

软链接,又称为符号链接,是一个指向另一个文件或目录的引用,类似于创建一个快捷方式。与硬链接不同,软链接创建的链接文件保存的是被链接文件的「路径信息」,而不是实际的文件内容或索引节点。当访问软链接时,操作系统会跟随链接并转到被链接的文件或目录。

如果你还是分不清的话,你就记:硬连接就是那个 "link.txt";软连接就是路径(快捷方式) 🤷‍♂️

.pnpm

.pnpm/ 为虚拟磁盘目录,它以平铺的形式储存着所有的包。pnpm 使用「软链接 + 平铺」目录结合的方式来构建一个嵌套结构:

image.png