npm、yarn、pnpm 都是现代化的 JavaScript 包管理器
npm、yarn、pnpm 都是现代化的 JavaScript 包管理器,它们的异同如下:
总之,选择哪个包管理工具取决于自己的喜好和项目需求。 如果你喜欢 npm 并且没有太多的依赖,那么使用 npm 可能比较合适。 如果你需要更快的速度和更好的安全,那么使用 Yarn 可能更好。 而如果你需要节省磁盘空间并缩短安装时间,则 pnpm 也是一个很好的选择。
node_modules
上管理实现npm、yarn、pnpm 在管理 node_modules 上的实现方式不同,具体如下:
package-lock.json
文件或 npm-shrinkwrap.json
文件,
用于锁定每个包的确切版本。
当安装依赖时,npm 将检查这些文件,以确保相同版本的依赖项在任何地方都得到安装。
npm 在安装依赖时会一次性将所有包下载到本地的 node_modules 目录中,
因此在 node_modules 中会出现很多重复的包。总而言之,这些包管理器都使用锁定文件来确保依赖项版本一致性, 并生成一个 node_modules 目录,其中包含所有项目的依赖项。 它们的主要区别在于它们如何确保依赖项和包版本的一致性, 以及如何管理 node_modules 目录中的包。
依赖都在
node_modules/.pnpm
下面,别的都是硬链接过去的, 所有实际的文件都在你的PNPM_HOME
里头
和 npm 不同,pnpm 通过创建符号链接的方式来管理依赖项,而不是像 npm 那样创建本地副本。
当安装依赖项时,pnpm 会在全局安装存储库中缓存所有依赖项的包。
在项目中安装依赖项时,pnpm 会创建一个 node_modules/.pnpm-local
目录的符号链接,
该目录包含一个符号链接的包和符号链接的二进制可执行文件。
依赖项的版本信息保留在 node_modules/.pnpm-store
目录中的元数据文件中。
依赖项的符号链接技术允许多个项目共享相同的依赖项,这样可以节省磁盘空间和下载时间。 相比于 npm 和 Yarn,即使在大型项目中,pnpm 仍然可以很好地管理依赖项,并且能够加快安装速度。 但是,使用符号链接也可能导致某些不兼容问题,因为符号链接可能会在不同操作系统、文件系统或设备中处理不同。
pnpm 的
node_modules
布局使用 符号链接 来创建 依赖项的嵌套结构。node_modules
中每个包的每个文件都是来自内容可寻址存储的 硬链接。Q: 为什么说
pnpm
这种 基于内容寻址 的方式对磁盘空间利用效率比较高 ? A: 1. 不会重复安装同一个包。 使用npm/yarn
的时候,如果100个包依赖express
, 那么就可能安装了100次express
,磁盘中就有100个地方写入了这部分代码。 但是pnpm
会只在一个地方写入这部分代码,后面使用会直接使用硬链接 2. 即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。 举个例子,比如express4.18.1
和epxress5.0.0-beta.1
的两个版本升级对比,epxress5.0.0-beta.1
只是重新下载了19个新的更新的包。
pnpm
与 npm
、yarn
有何不同 ?npm 2
的依赖管理
npm 2
依赖管理: 会按照安装包的依赖树形结构直接填充在本地的目录结构下
比如 express
和 koa
他们会同时依赖 accepts
,
那么在 install
之后生成的 node_modules
就会是如下结构:
npm 2
的这种方式的优点就是比较直观
,但是缺点也显而易见
npm3/yarn
的依赖管理针对 npm2 的两个缺点,npm3 做了更新, 不再使用嵌套的结构,而是将依赖进行展平, 这样就能解决层级依赖深和包的利用率的问题,那么上面的依赖关系就会变成下面这个样子:
在文件里看就是下面的这个样子(为了方便观看,把其他依稀依赖包手动删除了):
可以看到,
express@4.18.1
和koa@2.13.4
引用了accepts
的版本是一样的, 这样才会被平铺在node_modules
下,那如果引用的包的版本不一样又是什么样的情况呢? 就像debug
,http-errors
、statuses
等这几个包
express@4.18.1
引入的是: debug@2.6.9
、http-errors@2.0.0
、statuses@2.0.1
koa@2.13.4
引入的是: debug@4.3.2
、http-errors@1.6.3
、statuses@1.5.0
此时,在node_modules
结构是下面这个样子:
npm3 的这种平铺方式确实是解决了层级依赖深和包的利用率的问题,但是也引入了其他的问题:
express@4.18.1
依赖的 cookie
, 项目中手动引入, 但是依然可以使用,
这样就造成了如果哪天 express 改变了策略不再使用 cookie,而我们的项目中又使用了 cookie,
这样就会导致项目无法启动pnpm
的依赖pnpm
为了解决 npm3
带来的一些问题,
采用了另外一种方式来管理依赖:pnpm 的 node_modules
布局使用 符号链接 来创建依赖项的 嵌套结构。
node_modules
中 .pnpm
下每个包的每个文件都是来自内容可寻址存储的 硬链接 。
这是 node_modules
中的唯一的 "真实" 物理文件。
一旦所有包都硬链接到 node_modules
,就会创建符号链接来构建嵌套的依赖关系图结构。
用 pnpm 安装一下 express@4.18.1
和 koa@2.13.4
,生成的目录如下所示:
在 node_modules
中的依赖只有在 package.json
中手动引入的依赖 express
和 koa
,
细心的你会发现这两个文件后面有一个箭头,
而这个箭头就是 pnpm 使用的 软连接 的方式指向了 .pnpm
文件真正的文件:
这些文件会在磁盘中统一存储,如果有依赖不同版本的时候,pnpm 也只会下载不同版本中不同的内容。
Linux下有两种链接,一种是
硬链接(Hard Link)
,另一种是符号链接(Symbolic Link)
,也可以称之为软连接
硬链接
:只能引用同一文件系统中的文件。
它引用的是文件在文件系统中的物理索引(inode)。
当移动或者删除原始文件时,硬链接不会被破坏,
因为它所引用的是文件的物理数据而不是文件在文件结构中的位置。
硬链接记录的是目标的 inode。
同一文件的不同硬链接文件相当于该文件的多个不同文件名,即多个不同访问路径,他们的inode都是一样的。
不可以为目录创建硬连接。符号链接
:和原文件不是同一个文件,符号链接会有自己的inode,它所引用的是原文件的 path,
当原文件被移动或删除的时候,符号链接的文件就不可以。
例如 Windows 中的快捷方式。
也可以为目录创建软连接。
如何建立硬链接和软连接呢 可以使用 ln
命令来创建ln 原文件名 硬链接文件名
, 例如: ln file hardfile
,就是创建了file
文件的硬链接文件 hardfile
ln -s 原文件名 软链接文件名
,例如: ln -s file softfile
,就是创建了 file
文件的软链接文件 softfile
在系统中查看操作一遍(执行以下命令)
touch file && echo 'thisi is init file' > file
ln file hardfile
ln -s file softfile
ln -ls
可以看到 hardfile
和 file
的inode是一样的(最左侧的33038778),而 softfile
是有了一个自己的inode
此时如果删除 file
文件 rm file
,再去查看 hardfile
和 softfile
看一下有什么变化
cat hardfile
cat softfile
此时会发现,hardfile
文件是不受影响的,softfile
已经不存在了,因为它链接到的 file
已经不存在了
以下是官方给出的 pnpm
与 npm
和 yarn
的对比
标题 | pnpm | yarn | npm |
---|---|---|---|
工作空间支持(monorepo) | ✔️ | ✔️ | ✔️ |
隔离的 node_modules | ✔️ - 默认 | ✔️ | ❌ |
提升的 node_modules | ✔️ | ✔️ | ✔️ - 默认 |
自动安装 peers | ✔️ - 通过 auto-install-peers=true | ❌ | ✔️ |
Plug'n'Play | ✔️ | ✔️ - 默认 | ❌ |
零安装 | ❌ | ✔️ | ❌ |
修补依赖项 | ❌ | ✔️ | ❌ |
管理 Node.js 版本 | ✔️ | ❌ | ❌ |
有锁文件 | ✔️ - pnpm-lock.yaml | ✔️ - yarn.lock | ✔️ - package-lock.json |
支持覆盖 | ✔️ | ✔️ - 通过 resolutions | ✔️ |
内容可寻址存储 | ✔️ | ❌ | ❌ |
动态包执行 | ✔️ - 通过 pnpm dlx | ✔️ - 通过 yarn dlx | ✔️ - 通过 npx |
感谢帮助!