包管理器三个主要工具:
实际上,我们在所有包管理器之间已经实现了功能上的平衡,因此您很可能会根据非功能性的需求(例如安装速度、存储消耗或与现有工作流程的契合程度)来决定使用哪个包管理器。
当然,您选择如何使用每个包管理器可能会有所不同,但它们都包含主要功能。您可以使用任何这些包管理器执行以下操作:
- 处理和编写元数据
- 批量安装或更新所有依赖项
- 添加、更新和删除依赖项
- 运行脚本
- 发布软件包
- 执行安全审查
尽管存在这种平衡,但包管理器在内部有所不同。传统上,npm
和Yarn
会将依赖项安装在平铺的node_modules
文件夹中。但是这种依赖关系解析策略并不完美。
因此,pnpm
引入了一些新概念,以更有效地在嵌套的node_modules
文件夹中存储依赖项。Yarn Berry
更进一步,通过摒弃node_modules
并采用其Plug’n’Play(PnP)
模式。
在本文中,我们将根据一系列标准比较这些包管理器,包括安装工作流程、项目结构、配置文件等。
如何使用配套项目
我创建了一个配套的React
应用程序,以演示不同包管理器的独特概念。每个包管理器变体都有相应的Git
分支。这也是我用来创建本文后面部分中的性能表的项目。
尽管应用程序类型对本文主题并不重要,但我选择了一个中等大小和现实的项目,以便能够阐明不同方面;作为最近的一个例子,Yarn Berry
的PnP
机制引起了一些关于兼容性问题的激烈讨论,而这个项目适合帮助进行检查。
JavaScript
包管理器的简要历史
有史以来发布的第一个包管理器是npm
,发布于2010年1月。它奠定了当今包管理器运作的核心原则。
如果npm
已经存在了超过10年,为什么还会有任何替代方案呢?以下是一些重要原因:
- 使用不同的依赖关系解析算法,具有不同的
node_modules
文件夹结构(嵌套 vs. 平坦,node_modules
vs.PnP
模式) - 对
Hoisting
支持的不同,这对安全性有影响 - 不同的锁定文件格式,每种格式都有性能影响
- 不同的存储包的磁盘方法,这对磁盘空间利用效率有影响
- 不同对多包项目(即工作空间)的支持程度,这影响了大型
monorepos
的可维护性和速度 - 不同对新工具和命令的需求,每种工具和命令都对开发体验有影响
- 相关地,不同对通过插件和社区工具进行扩展性的需求
让我们深入了解一下,这些需求是如何在npm崛起之后被识别出来的,Yarn Classic如何解决了其中一些问题,pnpm如何在这些概念上进行了扩展,以及作为Yarn Classic继任者的Yarn Berry如何试图打破这些传统概念和流程。
先行者npm
npm是包管理器的鼻祖。错误地,许多人认为npm
是“Node package manager”
的缩写,但事实并非如此。尽管如此,它仍然与Node.js
运行时捆绑在一起。
它的发布构成了一场革命,因为在那之前,项目依赖关系是手动下载和管理的。像package.json
文件及其元数据字段(例如devDependencies
)、将依赖项存储在node_modules
中、自定义脚本、公共和私有包注册表等概念都是由npm
引入的。
2020年,GitHub
收购了npm
,因此从原则上讲,npm
现在由Microsoft
管理。在撰写本文时,最新的主要版本是v8,于2021年10月发布。
Yarn(v1 / Classic
),带来了许多创新
在2016年10月的一篇博客文章中,Facebook
宣布与Google
和其他几家公司合作,开发一个新的包管理器,旨在解决当时npm
存在的一致性、安全性和性能问题。他们将这个替代方案命名为Yarn
,即Yet Another Resource Negotiator
的缩写。
尽管他们将Yarn
的架构设计基于npm
确立的许多概念和流程,但Yarn
在初始发布时对包管理器的景观产生了重大影响。与npm
不同,Yarn
并行化操作以加快安装过程的速度,这是npm
早期版本的一个主要痛点。
Yarn
提高了DX
、安全性和性能的标准,还发明了许多概念,包括:
- 本机
monorepo
支持 - 缓存感知安装
- 离线缓存
- 锁文件
Yarn v1
于2020年进入维护模式。自那时起,v1.x系列被视为遗留版本,并更名为Yarn Classic
。其继任者Yarn v2
或Berry
现在是活跃的开发分支。
pnpm
,快速且节省磁盘空间
pnpm
的第一个版本于2017年由Zoltan Kochan
发布。它是npm
的一个可替代选择,因此如果您有一个npm
项目,您可以立即使用pnpm
!
pnpm
的创建者与npm
和Yarn
的主要问题是,跨项目使用的依赖关系的重复存储。尽管Yarn Classic
在性能上优于npm
,但它使用了与其他人相同的依赖关系解析方法,这对pnpm
的创建者来说是不可接受的:npm
和Yarn Classic
使用提升来平铺他们的node_modules
。
与提升不同,pnpm
引入了一种替代的依赖关系解析策略:内容可寻址存储。该方法导致了一个嵌套的node_modules
文件夹,它在您的主目录(~/.pnpm-store/)
中的全局存储中存储软件包。每个依赖项的每个版本在该文件夹中只存储一次,构成了一个单一的真相来源,并节省了相当多的磁盘空间。
通过一个node_modules
布局实现了这一点,使用符号链接来创建一个依赖项的嵌套结构,在文件夹中每个软件包的每个文件都是一个到存储的硬链接。官方文档的下面这张图解释了这一点。
pnpm
的影响可以从他们的2021年报告中看出:竞争对手想要采用pnpm
的安装概念,如符号链接的node_modules
结构和由于内容可寻址存储的创新而节省磁盘空间的管理。
Yarn(v2,Berry)
,用Plug’n’Play
重新发明轮子
Yarn 2
于2020年1月发布,被标榜为原始Yarn
的重大升级。Yarn
团队开始将其称为Yarn Berry
,以使其更明显地表明它实际上是一个具有新代码库和新原则的新包管理器。
Yarn Berry
的主要创新是其Plug’n’Play(PnP)
方法,这是一种解决node_modules
的策略。与生成node_modules
不同,它生成了一个包含依赖项查找表的.pnp.cjs文件,这样可以更高效地处理,因为它是一个单个文件,而不是一个嵌套的文件夹结构。此外,每个软件包都存储为.zip
文件,存储在.yarn/cache/
文件夹中,占用的磁盘空间比node_modules
文件夹少。
所有这些变化,而且速度很快,在发布后引起了很多争议。PnP
的重大变化要求维护者更新其现有软件包,以使其与之兼容。全新的PnP
方法被默认使用,并且最初没有简单地回退到node_modules
的方法,这导致许多著名开发人员公开批评Yarn 2
没有使其成为可选择的。
Yarn Berry
团队在随后的发布中解决了许多问题。为了解决PnP
的不兼容性问题,团队提供了一些简化默认操作模式的方法。通过一个node_modules
插件的帮助,只需要一行配置即可使用传统的node_modules
方法。
此外,随着时间的推移,JavaScript
生态系统对PnP
提供了越来越多的支持,您可以在此兼容性表中看到,一些大型项目已经开始采用Yarn Berry
。在我的配套项目中,我还能够正确地实现PnP
与我的演示React
项目。
尽管Yarn Berry
还很年轻,但它也已经在包管理器领域产生了影响 — pnpm
在2020年底采用了PnP
方法。
安装工作流程
首先,需要在每个开发人员的本地和CI/CD
系统上安装包管理器。
npm
npm
随Node.js
一起发布,因此不需要额外的步骤。除了下载适用于您的操作系统的Node.js
安装程序之外,使用CLI
工具来管理软件版本已经成为常见做法。在Node
的上下文中,Node Version Manager(nvm)
或Volta
已经成为非常方便的工具。
Yarn Classic
和Yarn Berry
可以以不同的方式安装Yarn 1
,例如,使用$ npm i -g yarn
作为npm
包安装。
从Yarn Classic
迁移到Yarn Berry
的推荐方式是:
- 安装或更新
Yarn Classic
到最新的1.x
版本 - 使用
yarn set version
命令升级到最新的现代版本:$ yarn set version berry
然而,安装Yarn Berry
的推荐方式是通过Corepack
。Corepack
是由Yarn Berry
的开发人员创建的。该倡议最初命名为package manager manager(pmm)
,并与Node
在LTS v16
中合并。
通过Corepack
的帮助,您不必“单独”安装npm
的替代包管理器,因为Node
包括了Yarn Classic、Yarn Berry
和pnpm
二进制文件作为垫片。这些垫片允许用户在首次安装它们而不必显式安装它们,并且不会混淆Node
发行版。
Corepack
随Node.js ≥ v16.9.0
预安装。但是,对于旧的Node
版本,您可以使用$ npm install -g corepack
来安装它。
在使用之前,先启用Corepack
。下面的示例显示了如何在Yarn Berry v3.1.1
中激活它。
您需要先选择
1 | $ corepack enable |
安装了垫片但需要激活具体版本
1 | $ corepack prepare yarn@3.1.1 --activate |
pnpm
您可以使用$ npm i -g pnpm
安装pnpm
作为npm
包。您也可以使用Corepack
安装pnpm
:
1 | $ corepack prepare pnpm@6.24.2 --activate |
项目结构
在本节中,您将一目了然地看到不同包管理器的主要特征。您可以轻松地发现哪些文件用于配置特定的包管理器,以及哪些文件是由安装步骤生成的。
所有包管理器都将所有重要的元信息存储在项目清单文件package.json
中。此外,根级别的配置文件可以用于设置私有注册表或依赖项解析方法。
通过安装步骤,依赖关系存储在文件结构中(例如,位于node_modules
中),并生成一个锁定文件。此部分不考虑工作区设置,因此所有示例仅显示一个位置存储依赖项。
npm
使用$ npm install
,或更短的$ npm i
,将生成一个package-lock.json
文件和一个node_modules
文件夹。可以将一个可选的.npmrc
配置文件放置在根级别。有关锁定文件的更多信息,请参阅下一节。
1 | ├── node_modules/ |
Yarn Classic
运行$ yarn
将创建一个yarn.lock
文件和一个node_modules
文件夹。.yarnrc
文件也可以是一个配置选项;Yarn Classic
也支持.npmrc
文件。可以选择使用缓存文件夹(.yarn/cache/)
和一个存储当前Yarn Classic
版本的位置(.yarn/releases/)
。不同的配置方式可以在比较配置部分中看到。
1 | . |
Yarn Berry
(使用node_modules
)
无论安装模式如何,您在Yarn Berry
项目中都将处理更多的文件和文件夹,而不是使用其他包管理器的项目。一些是可选的,一些是强制性的。
Yarn Berry
不再支持.npmrc
或.yarnrc
文件;相反,需要一个.yarnrc.yml
配置文件。对于生成的node_modules
文件夹的传统工作流程,您必须提供一个nodeLinker
配置,该配置使用node_modules
或受pnpm
启发的安装变体。
1 | # .yarnrc.yml |
运行$ yarn
将所有依赖项安装到一个node_modules
文件夹中。将生成一个yarn.lock
文件,这是更新的,但与Yarn Classic
不兼容。此外,生成一个.yarn/cache/
文件夹用于离线安装。releases
文件夹是可选的,并存储项目使用的Yarn Berry
版本,如我们将在比较配置部分中看到的那样。
1 | . |
Yarn Berry
(使用PnP
)
对于严格和松散的PnP模式,执行$ yarn
将生成.yarn/cache/
和.yarn/unplugged/
,以及.pnp.cjs
和yarn.lock
文件。PnP strict
是默认模式,但对于松散模式,需要一个配置。
1 | # .yarnrc.yml |
在PnP
项目中,.yarn/
文件夹很可能包含一个sdk/
文件夹,以提供IDE
支持,以及一个releases/
文件夹。根据您的用例,.yarn/
中可能还包含更多的文件夹。
1 | . |
pnpm
一个pnpm
项目的初始状态看起来与npm
或Yarn Classic
项目一样 - 您需要一个package.json
文件。通过$ pnpm i
安装依赖项后,将生成一个node_modules
文件夹,但其结构完全不同,因为它采用了内容可寻址的存储方法。
pnpm
还生成自己版本的锁定文件pnp-lock.yml
。您可以通过可选的.npmrc
文件提供额外的配置。
1 | ├── node_modules/ |
lock
文件和依赖项存储
如前所述,每个包管理器都会创建锁定文件。
锁定文件精确地存储了为您的项目安装的每个依赖项的版本,从而实现更可预测和确定性的安装。这是必需的,因为依赖项版本很可能是以版本范围(例如,≥ v1.2.5
)声明的,因此,如果您不“锁定”您的版本,实际安装的版本可能会有所不同。
锁定文件有时还会存储校验和,我们将在安全性部分更深入地介绍。
锁定文件自npm v5(package-lock.json)
、pnpm
从第一天(pnpm-lock.yaml)开始使用,并在
Yarn Berry中以新的
YAML格式
(yarn.lock)`出现。
在前一节中,我们看到了传统的方法,即将依赖项安装在node_modules
文件夹结构中。这是npm、Yarn Classic
和pnpm
都采用的方案,其中pnpm
比其他方案更高效。
PnP
模式下的Yarn Berry
做法不同。依赖项不是存储在node_modules
文件夹中,而是作为zip文件存储在.yarn/cache/
和.pnp.cjs
文件的组合中。
最好将这些锁定文件纳入版本控制,因为它解决了“在我的机器上可以工作”的问题——每个团队成员都安装相同的版本。
CLI命令
下表比较了npm、Yarn Classic、Yarn Berry
和pnpm
中的一组不同CLI
命令。这绝不是一个完整的列表,但构成了一个速查表。本节不涵盖与工作区相关的命令。
npm
和pnpm
特别提供了许多命令和选项的别名,这意味着命令可以有不同的名称,例如,$ npm install
与$ npm add
相同。此外,许多命令选项都有简短的版本,例如,-D
代替--save-dev
。
在表格中,我将所有简短版本称为别名。使用所有包管理器,您可以通过用空格分隔它们来添加、更新或删除多个依赖项(例如,npm update react react-dom
)。为了清晰起见,示例仅显示了单个依赖项的用法。
依赖管理
该表格涵盖了用于安装或更新package.json
中指定的所有依赖项,或通过在命令中指定它们来安装或更新多个依赖项的依赖项管理命令。
Action | npm | Yarn Classic | Yarn Berry | pnpm |
---|---|---|---|---|
install deps in package.json |
npm install alias: i , add |
yarn install or yarn |
like Classic | pnpm install alias: i |
update deps in package.json acc. semver |
npm update alias: up , upgrade |
yarn upgrade |
yarn semver up (via plugin) |
pnpm update alias: up |
update deps in package.json to latest |
N/A | yarn upgrade --latest |
yarn up |
pnpm update --latest alias: -L |
update deps acc. semver | npm update react |
yarn upgrade react |
yarn semver up react |
pnpm up react |
update deps to latest | npm update react@latest |
yarn upgrade react --latest |
yarn up react |
pnpm up -L react |
update deps interactively | N/A | yarn upgrade-interactive |
yarn upgrade-interactive (via plugin) |
$ pnpm up --interactive alias: -i |
add runtime deps | npm i react |
yarn add react |
like Classic | pnpm add react |
add dev deps | npm i -D babel alias: --save-dev |
yarn add -D babel alias: --dev |
like Classic | pnpm add -D babel alias: --save-dev |
add deps to package.json without semver |
npm i -E react alias: --save-exact |
yarn add -E react alias: --exact |
like Classic | pnpm add -E react alias: --save-exact |
uninstall deps and remove from package.json |
npm uninstall react alias: remove , rm , r , un , unlink |
yarn remove react |
like Classic | pnpm remove react alias: rm , un , uninstall |
uninstall deps w/o update of package.json |
npm uninstall --no-save |
N/A | N/A | N/A |
Package 指令
以下示例展示了如何在开发过程中管理构成实用工具的Package — 即二进制文件,例如 ntl,用于交互式地执行脚本。表格中使用的术语如下:
- Package:依赖项或二进制文件
- Binary:从 node_modules/.bin/ 或 .yarn/cache/(PnP)中执行的可执行实用程序
重要的是要理解,出于安全原因,Yarn Berry 只允许我们执行在 package.json 中指定的二进制文件,或者在您的 bin 元字段中公开的二进制文件。pnpm 具有相同的安全行为。
| Action | npm | Yarn Classic | Yarn Berry | pnpm |
| — | — | — | — | — |
| install packages globally | npm i -g ntl
alias: --global
| yarn global add ntl
| N/A (global removed) | pnpm add --global ntl
|
| update packages globally | npm update -g ntl
| yarn global upgrade ntl
| N/A | pnpm update --global ntl
|
| remove packages globally | npm uninstall -g ntl
| yarn global remove ntl
| N/A | pnpm remove --global ntl
|
| run binaries from terminal | npm exec ntl
| yarn ntl
| yarn ntl
| pnpm ntl
|
| run binaries from script | ntl
| ntl
| ntl
| ntl
|
| dynamic package execution | npx ntl
| N/A | yarn dlx ntl
| pnpm dlx ntl
|
| add runtime deps | npm i react
| yarn add react
| like Classic | pnpm add react
|
| add dev deps | npm i -D babel
alias: --save-dev
| yarn add -D babel
alias: --dev
| like Classic | pnpm add -D babel
alias: --save-dev
|
| add deps to package.json
without semver | npm i -E react
alias: --save-exact
| yarn add -E react
alias: --exact
| like Classic | pnpm add -E react
alias: --save-exact
|
| uninstall deps and remove from package.json
| npm uninstall react
alias: remove
, rm
, r
, un
, unlink
| yarn remove react
| like Classic | pnpm remove react
alias: rm
, un
, uninstall
|
| uninstall deps w/o update of package.json
| npm uninstall
--no-save
| N/A | N/A | N/A |
配置文件
配置包管理器的操作既可以在您的 package.json
中进行,也可以在专用的配置文件中进行。以下是一些配置选项的示例:
- 定义要使用的确切版本
- 使用特定的依赖项解析策略
- 配置访问私有注册表
- 告诉包管理器在 monorepo 中的工作区所在位置
npm
大多数配置都在专用的配置文件(.npmrc
)中进行。
如果您想要使用 npm 的工作区功能,则必须通过使用工作区元数据字段向 package.json 中添加配置,以告诉 npm 在哪里找到构成子项目或工作区的文件夹。
1 | { |
每个包管理器都可以直接使用公共 npm
注册表。在公司环境中使用共享库时,您很可能希望在不将其发布到公共注册表的情况下重用它们。要配置私有注册表,可以在.npmrc
文件中执行此操作。
1 | # .npmrc |
npm 有许多配置选项,并且最好在文档中查看它们。
Yarn Classic
您可以在 package.json
中设置 Yarn 工作区。它类似于 npm,但工作区必须是私有包。
1 | { |
任何可选配置都放在.yarnrc
文件中。常见的配置选项是设置 yarn-path
,它强制每个团队成员使用特定的二进制版本。yarn-path
指向一个包含特定 Yarn 版本的文件夹(例如 .yarn/releases/
)。您可以使用yarn policies
命令安装 Yarn Classic
版本。
Yarn Berry
在Yarn Berry
中配置工作区与 Yarn Classic
中的配置方式类似,都是使用 package.json
。大多数Yarn Berry
配置都在.yarnrc.yml
中进行,有许多配置选项可用。Yarn Classic
的示例也是可能的,但元数据字段被重命名为 yarnPath
。
1 | # .yarnrc.yml |
Yarn Berry
可以通过使用 yarn plugin import
扩展插件。此命令会更新 .yarnrc.yml
。
1 | # .yarnrc.yml |
正如在历史部分中所描述的,由于不兼容性,PnP
严格模式中的依赖关系可能会出现问题。对于这种 PnP 问题,有一个典型的解决方案:packageExtensions
配置属性。您可以按照下面的示例执行相应的项目。
1 | # .yarnrc.yml |
pnpm pnpm
使用与npm
相同的配置机制,因此可以使用.npmrc
文件。配置私有注册表的方法也与 npm
相同。
使用 pnpm
的工作区功能,可以支持多包项目。要初始化 monorepo
,必须在pnpm-workspace.yaml
文件中指定包的位置。
1 | # pnpm-workspace.yaml |
Monorepo 支持
什么是 monorepo
? Monorepo
是一个存放多个项目的存储库,这些项目被称为工作区或包。它是一种项目组织策略,将所有内容放在一个地方,而不是使用多个存储库。
当然,这会带来额外的复杂性。Yarn Classic
是第一个启用此功能的包管理器,但现在每个主要的包管理器都提供了工作区功能。本节展示了如何使用不同的包管理器配置工作区。
npm
npm
团队在 v7 中发布了备受期待的 npm
工作区功能。它包含了许多 CLI 命令,可以帮助从根包内部管理多包项目。大多数命令可以与工作区相关的选项一起使用,以告诉 npm
是否应该针对特定、多个或所有工作区运行。
1 | # 安装所有工作区的所有依赖项 |
与其他包管理器不同,npm v8
目前不支持高级过滤或并行执行多个与工作区相关的命令。
Yarn Classic
2017 年 8 月,Yarn
团队宣布了一流的monorepo
支持,即工作区功能。在此之前,只能使用第三方软件(如 Lerna
)在多包项目中使用包管理器。这个功能的添加为其他包管理器实现了这样一个功能铺平了道路。
1 | # 安装所有工作区的所有依赖项 |
Berry Yarn
Berry
从一开始就具有工作区功能,因为其实现是建立在 Yarn Classic
的概念之上的。在 Reddit
评论中,Yarn Berry
的主要开发人员对工作区导向功能(包括以下内容)进行了简要概述:
$ yarn add --interactive
:在安装包时,可以重用其他工作区中的版本$ yarn up
:更新所有工作区的包$ yarn workspaces focus
:仅为单个工作区安装依赖项$ yarn workspaces foreach
:在所有工作区上运行命令Yarn Berry
大量使用协议,这些协议可以在package.json
文件的dependencies
或devDependencies
字段中使用。其中之一是workspace:
protocol.
与 Yarn Classic
的工作区不同,Yarn Berry
明确规定依赖项必须是此 monorepo
中的包之一。否则,如果版本不匹配,Yarn Berry
可能会尝试从远程注册表中获取版本。
1 | { |
pnpm
借助其 workspace:
protocol,pnpm
类似于 Yarn Berry
一样便于处理 monorepo
项目。许多 pnpm
命令接受选项,如 --recursive (-r)
或 --filter
,在 monorepo
上下文中特别有用。其原生过滤命令也是 Lerna
的一个很好的补充或替代品。
1 | # 清理所有工作区 |
性能和磁盘空间效率
性能是决策的重要部分。本节根据一个小型项目和一个中等大小项目进行基准测试。以下是有关样本项目的一些说明:
两组基准测试均不使用工作区功能 小项目指定了 33 个依赖项 中型项目指定了 44 个依赖项 我为三种用例(UC
)进行了测量,分别针对每个包管理器变体。要了解详细评估和解释,请查看项目 1 (P1)
和项目2 (P2)
的结果。
UC 1
:无缓存/存储,无锁文件,无node_modules
或.pnp.cjs
UC 2
:存在缓存/存储,无锁文件,无node_modules
或.pnp.cjs
UC 3
:存在缓存/存储,存在锁文件,无node_modules
或.pnp.cjs
使用工具gnomon
来测量安装所需的时间(例如,$ yarn | gnomon
)。此外,还测量了生成文件的大小,例如,$ du -sh node_modules
。
根据我的项目和测量结果,Yarn Berry PnP
严格模式在所有用例和两个项目中的安装速度方面都是赢家。
安全功能
npm
在处理有问题软件包时,npm
有时候过于宽容,并且曾经遇到一些直接影响许多项目的安全漏洞。例如,在版本 5.7.0
中,当您在 Linux
操作系统上执行 sudo npm
命令时,可能会改变系统文件的所有权,导致操作系统无法使用。
另一起事件发生在 2018 年,涉及比特币的盗窃。基本上,流行的 Node.js
软件包EventStream
在其版本 3.3.6 中添加了一个恶意依赖项。这个恶意软件包包含一个加密的负载,试图从开发人员的计算机中窃取比特币。
为了帮助解决这些问题,较新的 npm
版本使用 SHA-512
加密算法在 package-lock.json
中检查您安装的软件包的完整性。
总的来说,npm
已经在尽力弥补他们的安全漏洞,尤其是与 Yarn
相比更为明显的漏洞。
Yarn
自 Yarn Classic
和 Yarn Berry
起,它们就从一开始就通过在 yarn.lock
中存储校验和来验证每个软件包的完整性。Yarn
还试图阻止您在安装过程中检索未在package.json
中声明的恶意软件包:如果发现不匹配,则会中止安装
。
PnP
模式的 Yarn Berry
不会遭受传统 node_modules
方法的安全问题。与 Yarn Classic
相比,Yarn Berry
改善了命令执行的安全性。您只能执行在 package.json
中显式声明的依赖项的二进制文件。这个安全功能与 pnpm
类似,我接下来会描述。
pnpm
pnpm
也使用校验和在执行每个已安装软件包的代码之前验证其完整性。
正如我们上面提到的,由于 hoisting
,npm
和 Yarn Classic
都存在安全问题。pnpm
避免了这个问题,因为它的模型不使用 hoisting
;相反,它生成嵌套的 node_modules
文件夹,消除了非法依赖访问的风险。这意味着只有在package.json
中显式声明的依赖项才能访问其他依赖项。
这在monorepo
设置中尤为重要,因为我们讨论过,hoisting
算法有时会导致虚假依赖项和重复。
开源项目的采用
这些活跃维护的开源项目现在使用哪些包管理器,这可能会在选择包管理器时给您提供参考。
| npm | Yarn Classic | Yarn Berry | pnpm |
| — | — | — | — |
| Svelte | React | Jest (with node_modules
) | Vue 3 |
| Preact | Angular | Storybook (with node_modules
) | Browserlist |
| Express.js | Ember | Babel (with node_modules
) | Prisma |
| Meteor | Next.js | Redux Toolkit (with node_modules
) | SvelteKit |
| Apollo Server | Gatsby | | |
| | Nuxt | | |
| | Create React App | | |
| | webpack-cli | | |
| | Emotion | |
有趣的是,这些开源项目中没有一个使用 PnP
方法。
结论
我们在所有主要的包管理器之间几乎实现了功能平等。但是,它们在内部仍有很大的不同。
pnpm
乍一看像是 npm
,因为它们的 CLI
使用方式类似,但是管理依赖关系有很大不同;pnpm
的方法可以提高性能并实现最佳的磁盘空间效率。Yarn Classic
仍然非常流行,但被视为遗留软件,并且支持可能会在不久的将来被停止。Yarn Berry PnP
是新晋的宠儿,但尚未充分发挥其再次革命化包管理器领域的潜力。
本文的目标是为您提供多种视角,以便自行决定使用哪个包管理器。不推荐特定的包管理器。这取决于您如何权衡不同的要求 —— 所以您仍然可以选择自己喜欢的!