首页 >web前端 >js教程 >JavaScript包管理器比较:Npm vs Yarn vs Pnpm

JavaScript包管理器比较:Npm vs Yarn vs Pnpm

青灯夜游
青灯夜游转载
2022-08-09 16:22:513305浏览

本篇文章带大家了解一下三种JavaScript包管理器(npm、yarn、pnpm),并将这三种包管理器进行对比,聊聊npm、yarn、pnpm三者的区别和关联,希望对大家有所帮助,如有问题欢迎指出!

JavaScript包管理器比较:Npm vs Yarn vs Pnpm

包管理器领域的三个主要参与者:

实际上我们已经在所有包管理器中实现了基本相似的功能,因此您很可能会根据非功能性要求来决定使用哪个包管理器,例如安装速度、存储消耗或实际情况。

当然,您选择使用每个包管理器的方式会有所不同,但它们都有基本一致的概念。以上这些包管理器都可以执行以下指令:

  • 读写数据
  • 批量安装或更新所有依赖项
  • 添加、更新和删除依赖项
  • 运行脚本
  • 发布包

然而尽管如此,包管理器在底层还是有所不同的。传统上 npmYarn 将依赖项安装在一个平铺的node_modules文件夹中。(这里注意先后顺序,是 yarn 先平铺的,之前 npm 是递归)。但是平铺也会造成一系列的安全问题。

  • 依赖结构的不确定性

  • 扁平化算法本身的复杂性很高,耗时较长。

  • 项目中仍然可以非法访问

  • 有声明过依赖的包

因此,pnpmnode_modules 文件夹中引入了一些新概念来更高效的存储依赖,。Yarn Berry 甚至通过完全放弃 node_modules 的 (PnP) 模式(另一个文章会具体说明)来走得更远。

JavaScript package简史


最早发布的包管理器是 npm,早在 2010 年 1 月。它就确立了今天包管理器工作的核心原则。但是既然 npm 已经存在 10 多年了,为什么还有其他选择?以下是出现这种情况的一些关键原因:

  • node_modules 文件夹结构的依赖关系解析算法不同(嵌套 & 平铺、node_modules vs. PnP mode)
  • 依赖提升方式不同(hoisting
  • locking 格式不同(性能都不同,比如 yarn 自己写的那一套)
  • 磁盘存储包文件方式不同(空间效率不同)
  • 多包项目(又名 workspaces)的支持不同,这会影响 monorepos 的可维护性和速度
  • 新工具和命令的需求不同(通过插件和社区工具对可扩展性的需求不同)
  • 可配置性和灵活性不同

让我们深入了解一下 npm 崛起后这些方面如何确定的历史,Yarn Classic 如何解决其中的一些问题,pnpm如何扩展这些概念,以及 Yarn Berry 作为 Yarn Classic 的继任者如何打破这些传统的概念和流程。

先驱者 npm

npm 是包管理器的鼻祖。许多人错误地认为 npm 是“Node package manager”的首字母缩写词,但事实并非如此

它的发布构成了一场革命,因为在此之前,项目依赖项都是手动下载和管理的。npm 引入了诸如文件及其元数据字段、将依赖项存储在node_modules, 自定义脚本, 公共和私有包等等。

2020 年,GitHub 收购了 npm,所以原则上 npm 现在归微软管理。在撰写本文时,最新的主要版本是 v8,于 2021 年 10 月发布。

创新者 Yarn Classic

在 2016 年 10 月,Facebook 宣布与 Google 和其他一些公司合作开发一个新的包管理器(engineering.fb.com/2016/10/11/…),以解决 npm 当时存在的一致性、安全性和性能问题。他们将替代品命名为Yarn

尽管 Yarn 还是基于 npm 的许多概念和流程来架构设计的,但 Yarn 还是对包管理器领域产生了重大影响。与 npm 相比,Yarn 并行化操作以加快安装过程,这一直是 npm 早期版本的主要痛点。

Yarn 为读写、安全性和性能设定了更高的标准,还发明了许多概念(后来npm也为此做了很多改进),包括:

  • monorepo 支持
  • 缓存安装
  • 离线下载
  • 文件锁(Locking

Yarn v1 于 2020 年进入维护模式 。从那时起,v1.x 系列被认为是旧版,并更名为 Yarn Classic。它的继任者 Yarn v2 (Berry) 现在是更加活跃的开发分支。

更高效的pnpm

pnpm 的第 1 版由 Zoltan Kochan于 2017 年发布。它是 npm 的替代品,所以如果你有一个 npm 项目,你可以马上使用 pnpm

创建 pnpm 的主要原因是 npmYarn 对于跨项目使用的依赖项存储结构非常冗余。尽管 Yarn Classicnpm 具有速度优势,但它使用相同的依赖解析方法,这对 pnpm 来说是不行的:npmYarn Classic 使用 hoisting 来平铺他们的 node_modules.

pnpm 没有优化之前的结构,而是引入了另一种依赖解决策略:内容寻址的一种存储结构。此方法生成的 node_modules 文件夹其实是依赖于全局存储在主文件夹上的 ~/.pnpm-store/ 目录。每个版本的依赖项都物理形式存储在该目录文件夹中一次,构成单一的源地址来节省相当多的磁盘空间。

node_modules 结构是通过使用 symlinks 创建依赖关系的嵌套结构(其中文件夹内每个文件/包都是通过硬链接存储)官方文档中的下图阐明了这一点。(待填坑:软硬链接)

1.png

2021 年报告中可见 pnpm 的影响力:因为他们在内容可寻址存储方面的创新,竞争对手都希望采用 pnpm 的概念,比如象征性链接的结构和包的高效磁盘管理。

Yarn (v2, Berry),用 Plug'n'Play 重新发明的轮子

Yarn 2于 2020 年 1 月发布,被宣传为原始 Yarn 的重大升级。Yarn 团队将其称为 Yarn Berry 以更明显地表明它本质上是一个具有新的代码库和新的原则规范的新包管理器。

Yarn Berry 的主要创新是其即插即用 (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

尽管 Yarn Berry 还很年轻,但它也已经对包管理器领域产生了影响——pnpm 在 2020 年末采用了PnP 方法

安装工作流程


首先必须在每个开发人员的本地和 CI/CD 系统上安装包管理器。

npm

npmNode.js 一起提供,因此不需要额外的步骤。除了为您的操作系统下载Node.js 安装程序外,使用 CLI 工具管理软件版本已成为一种常见做法。在 Node 的上下文中,Node Version Manager (nvm)Volta 已成为非常方便的实用程序。

Yarn Classic 和 Yarn Berry

您可以通过不同的方式安装 Yarn 1,例如,作为 npm 包来安装:.$ npm i -g yarn

要从Yarn Classic 迁移到 Yarn Berry,推荐的方法是:

  • 安装或更新 Yarn Classic 到最新的版本

  • 使用命令升级到最新的现代版本

    yarn set version berry

但是,在此推荐的安装 Yarn Berry方法是通过 Corepack。

Corepack是由 Yarn Berry 的开发者创建的。该计划最初被命名为包管理器管理器(pmm) ?,并在 LTS v16 中与 Node 合并。

在 Corepack 的帮助下,因为 Node 包含 Yarn ClassicYarn Berrypnpm 二进制文件所以您不必“单独”安装的 npm 的替代包管理器。这些垫片允许用户运行 Yarn 和 pnpm 命令而无需先显式安装它们,也不会弄乱 Node 发行版。

Corepack 预装了 Node.js ≥ v16.9.0。但是,对于较旧的 Node 版本,您可以使用⬇️

npm install -g corepack

在使用之前先启用 Corepack。该示例显示了如何在 Yarn Berry v3.1.1 中激活它。

# you need to opt-in first
$ corepack enable
# shim installed but concrete version needs to activated
$ corepack prepare yarn@3.1.1 --activate

pnpm

您可以将 pnpm 作为 npm包来安装: $ npm i -g pnpm。您还可以使用 Corepack 安装 pnpm : 

$ corepack prepare pnpm@6.24.2 --activate

项目结构


在本节中,您将一目了然地看到不同包管理器的主要特征。您可以轻松发现配置特定包管理器涉及哪些文件,以及哪些文件是由安装步骤生成的。

所有包管理器都将所有重要的元信息存储在项目清单package.json文件中。 此外,根级别的配置文件可以被用来设置不同的私有包或者不同的依赖项解析配置。

在安装步骤中,依赖项 dependencies 被存储在文件结构中,例如 node_modules 并生成锁定文件 locking。本节不考虑工作区设置,因此所有示例仅显示存储依赖项的单个位置。

npm

使用$ npm install 或较短的 $ npm i 会生成一个 package-lock.json 文件和一个 node_modules 文件夹。还有 .npmrc 这种可配置的文件可以放在根级别目录里面。有关 locking 文件的更多信息,请参阅下一节。

.
├── node_modules/
├── .npmrc
├── package-lock.json
└── package.json

yarn

运行 $ yarn 会创建一个 yarn.lock 文件和一个 node_modules 文件夹。.yarnrc 文件也可以是配置的选项,Yarn Classic 也支持 .npmrc 文件。或者可以使用缓存文件夹 .yarn/cache/ 和本地存储的最近的 Yarn Classic 版本 .yarn/releases/

.
├── .yarn/
│   ├── cache/
│   └── releases/
│       └── yarn-1.22.17.cjs
├── node_modules/
├── .yarnrc
├── package.json
└── yarn.lock

yarn berry: node_modules

因为这种特殊的安装模式,比使用其他包管理器您必须在 Yarn Berry 项目中处理更多的文件和文件夹。有些是可选的,有些是强制性的。

Yarn Berry 不再支持 .npmrc 或者 .yarnrc;他需要一个 .yarnrc.yml。对于传统的生成 node_modules 文件夹的工作流程,您必须提供 nodeLinker 配置来使用 node_modules 或者 pnpm 的配置(这块没看懂)。

# .yarnrc.yml
nodeLinker: node-modules # or pnpm

运行 $ yarn 会将所有依赖项安装在一个 node_modules 文件夹中。并且生成一个 yarn.lock 文件,该文件较新但与 Yarn Classic 不兼容。此外,还会生成一个用于离线安装的 .yarn/cache/ 文件夹。该文件夹是可选的,用于存储项目使用的 Yarn Berry 版本。

.
├── .yarn/
│   ├── cache/
│   └── releases/
│       └── yarn-3.1.1.cjs
├── node_modules/
├── .yarnrc.yml
├── package.json
└── yarn.lock

yarn berry: pnp

无论是对于PnP 的严格模式还是松散模式,跟着 .pnp.cjsyarn.lock 来执行 $ yarn 都会生成一个 .yarn/cache/ 还有 .yarn/unplugged。PnP strict 是默认模式,如果想要配置 loose 模式,需要如下形式开启⬇️:

# .yarnrc.yml
nodeLinker: pnp
pnpMode: loose

在 PnP 项目中,除了 releases 文件夹之外,.yarn 文件夹很可能还包含一个提供IDE 支持的 sdk/ 文件夹。根据您的用例,.yarn 甚至可以包含更多的文件夹。

.
├── .yarn/
│   ├── cache/
│   ├── releases/
│   │   └── yarn-3.1.1.cjs
│   ├── sdk/
│   └── unplugged/
├── .pnp.cjs
├── .pnp.loader.mjs
├── .yarnrc.yml
├── package.json
└── yarn.lock`

pnpm

npmYarn Classic 项目的初始状态一样,pnpm 也需要 package.json 文件。使用 $ pnpm i 安装依赖项后,会生成一个 node_modules 文件夹,但由于其内容是可寻址存储方式,其结构完全不同。

pnpm 还会生成自己的锁定文件 pnp-lock.yml。 您可以使用可选文件 .npmrc 提供附加配置。

.
├── node_modules/
│   └── .pnpm/
├── .npmrc
├── package.json
└── pnpm-lock.yml

锁定文件和依赖存储


如上一节所述,每个包管理器都会创建锁定文件

lock 文件准确存储您的项目安装的每个依赖项的版本从而实现更可预测和确定性的安装。因为依赖版本很可能使用版本范围声明(例如,≥ v1.2.5)所以这个 lock 文件是很重要的,如果您不“lock”您的版本,实际安装的版本可能会有所不同。

锁定文件有时也存储校验和(我记得是一段hash),我们将在安全部分更深入地介绍。

npm v5+ 开始锁定文件一直是 npm 主要的功能 ( package-lock.json ) ,pnpm 里是 pnpm-lock.yaml ,在 Yarn Berry 中的 yarn.lock 以新的 YAML 格式出现。

在上一节中,我们看到了传统方法,将依赖项安装在 node_modules 文件夹结构中。这是 npmYarn Classicpnpm都使用的方案,(其中 pnpm 比其他方案更有效)。

Yarn Berry 在 PnP 模式下的做法有所不同。依赖项不是 node_modules 文件夹,而是以 zip 文件的形式存储为 .yarn/cache/.pnp.cjs 文件的组合。

最好将这些锁定文件置于版本控制之下,因为每个团队成员都安装相同的版本,所以它解决了“在你和我的机器上工作”问题。

CLI


下表比较了 npmYarn ClassicYarn Berrypnpm 中可用的不同 CLI 命令。这绝不是一个完整的列表,而是一个备忘单。本节不涉及与workflow 相关的命令。

npmpnpm 具有许多特别的命令和选项别名,这意味着命令可以有不同的名称,即$ npm install$ npm add。 此外,许多命令选项都有缩写版本,例如 -D 来代替 --save-dev。 在表格中,我将所有缩写版本称为别名。使用这些包管理器,您都可以增加、更新或删除多个依赖项。

依赖配置管理

此表涵盖了用于安装或更新 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: dependency or binary
  • Binary: 一种执行工具来自 node_modules/.bin/ 或者 .yarn/cache/ (PnP)

重要的是要理解,Yarn Berry 只允许我们执行在 package.json 中或者暴露在 bin/ 文件中的指定的二进制文件。

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

常用命令

该表涵盖了一些有用的内置命令。如果没有官方的命令,通常可以通过 npm 包或 Yarn Berry 插件来使用第三方命令。

Action npm Yarn Classic Yarn Berry pnpm
发包 npm publish yarn publish yarn npm publish pnpm publish
list installed deps npm ls alias: list, la, ll yarn list
pnpm list alias: ls
list outdated deps npm outdated yarn outdated yarn upgrade-interactive pnpm outdated
print info about deps npm explain ntl alias: why yarn why ntl like Classic pnpm why ntl
init project npm init -y npm init (interactive) alias: --yes yarn init -y yarn init (interactive) alias: --yes yarn init pnpm init -y pnpm init (interactive) alias: --yes
print licenses info N/A (via third-party package) yarn licenses list N/A (or via plugin, other plugin) N/A (via third-party package)
update package manager version N/A (with third-party tools, e.g., nvm) with npm: yarn policies set-version 1.13.0 with Corepack: yarn set version 3.1.1 N/A (with npm, Corepack)
perform security audit npm audit yarn audit yarn npm audit pnpm audit
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 中。

如果你想使用 npmworkspaces 功能,你必须在 package.json 中添加workspaces 元数据字段来告诉 npm 在哪里可以找到子项目或工作空间的文件夹。

  // ...
  "workspaces": [
    "hooks",
    "utils"
  ]
}

每个包管理器都可以使用公共 npm 注册表。或许你很可能希望重用它们而不将它们发布到公共注册表。您可以在 .npmrc 文件中执行此操作配置来私有注册表。( 现在基本都有私有源了)

# .npmrc
@doppelmutzi:registry=https://gitlab.doppelmutzi.com/api/v4/projects/41/packages/npm/

npm 存在许多配置选项,最好在文档中查看它们。\

yarn

您可以在 package.json 中设置 yarnworkspaces(必须是私有包)。

{
  // ...
  "private": true,
  "workspaces": ["workspace-a", "workspace-b"]
}

任何可选配置都进入一个 .yarnrc 文件。一个常见的配置选项是设置一个 yarn-path: 它强制每个团队成员使用指定的二进制版本。yarn-path 指向包含特定 Yarn 版本的文件夹(例如 .yarn/releases/)。您可以使用命令来安装统一的 Yarn Classic 版本(classic.yarnpkg.com/en/docs/cli…)。

yarn berry

Yarn Berry 中配置 workspaces 和在 Yarn Classic 中的配置方式类似(package.json)。 大多数 Yarn Berry 配置发生在 .yarnrc.yml 中,并且有许多可用的配置选项。

# .yarnrc.yml
yarnPath: .yarn/releases/yarn-3.1.1.cjs

yarn berry可以用 $> yarn plugin import <name> 这种导入方式来扩展插件(yarnpkg.com/cli/plugin/…),这个命令也会更新 .yarnrc.yml

# .yarnrc.yml
plugins:
  - path: .yarn/plugins/@yarnpkg/plugin-semver-up.cjs
    spec: "https://raw.githubusercontent.com/tophat/yarn-plugin-semver-up/master/bundles/%40yarnpkg/plugin-semver-up.js"

如历史部分所述,因为兼容性的关系,PnP 严格模式下的依赖关系可能存在某些问题。此类 PnP 问题有一个典型的解决方案:包扩展配置策略

# .yarnrc.yml
packageExtensions:
  "styled-components@*":
    dependencies:
      react-is: "*"

pnpm

pnpm 使用与 npm 相同的配置机制,因此您可以使用 .npmrc 文件。配置私有注册表的工作方式也与使用 npm 相同。借助 pnpm 的工作空间功能可以支持多包项目。要初始化 monorepo,您必须在 pnpm-workspace.yaml 文件中指定包的位置

# pnpm-workspace.yaml
packages:
  - 'packages/**'

Monorepo


什么是Monorepo

(这里其实就是三种概念,单仓库多项目,单仓库单项目,多仓库多项目)

monorepo 是一个包含多个项目的存储库,这些项目被称为 workspace 或者包。将所有内容保存在一个地方而不是使用多个存储库是一种项目组织策略。

当然,这会带来额外的复杂性。Yarn Classic 是第一个启用此功能的,但现在每个主要的包管理器都提供了工作区功能。本节展示如何使用每个不同的包管理器配置工作区。

npm

npm 团队在 v7 中发布了期待已久的npm 工作区功能。它包含许多 CLI 命令,可帮助从根包中管理多包项目。大多数命令可以与 workspace 相关的选项一起使用以告诉 npm 它是否应该针对特定、多个或所有工作空间运行。

# Installing all dependencies for all workspaces
$ npm i --workspaces.
# run against one package
$ npm run test --workspace=hooks
# run against multiple packages
$ npm run test --workspace=hooks --workspace=utils
# run against all
$ npm run test --workspaces
# ignore all packages missing test
$ npm run test --workspaces --if-present

tips: 与其他包管理器相比,npm v8 目前不支持高级过滤或并行执行多个与工作区相关的命令。

yarn

2017 年 8 月,Yarn 团队宣布在workspace功能方面提供 monorepo 支持。在此之前,只能在Lerna等第三方软件的多包项目中使用包管理器。Yarn 的这一新增功能也为其他包管理器实现此类功能铺平了道路。

如果你有兴趣,可以参考如何在有和没有 Lerna 的情况下使用 Yarn Classic 的工作区功能。但是这篇文章只会介绍一些必要的命令,以帮助您管理 Yarn Classic 工作区设置中的依赖关系。

# 为所有工作空间安装所有依赖项
$ yarn 
# 显示依赖关系树
$ yarn workspaces info 
# 再一个包运行启动
$ yarn workspace awesome - package start
  # 将Webpack添加到包
$ yarn workspace awesome - package add - D webpack
  # add React 对所有包
$ yarn add react -W

yarn berry

Yarn Berry 从一开始就以工作区为特色,因为它的实现是建立在 Yarn Classic 的概念之上的。在Reddit 评论中,Yarn Berry 的主要开发人员简要概述了面向工作空间的功能,包括:

Yarn Berry 使用大量可用于 package.json 文件的 dependenciesdevDependencies 字段的协议。其中就有 workspace协议

Yarn Classic 的工作区相比,Yarn Berry 明确定义依赖项必须是此 monorepo 中的包之一。否则如果版本不匹配,Yarn Berry 可能会尝试从远程注册表获取其版本。

{
  // ...
  "dependencies": {
    "@doppelmutzi/hooks": "workspace:*",
    "http-server": "14.0.0",
    // ...
  }  
}

pnpm

通过 workspace 这种协议,pnpm 促成了类似于 Yarn Berrymonorepo 项目。许多 pnpm 命令接受 --recursive (-r) 或者 --filter 这种在 monorepo 上下文中特别有用的选项。它的原生过滤命令也是对 Lerna 的一个很好的补充。

# prune all workspaces  
pnpm -r exec -- rm -rf node_modules && rm pnpm-lock.yaml  
# run all tests for all workspaces with scope @doppelmutzi
pnpm recursive run test --filter @doppelmutzi/`

性能 & 磁盘效率


性能是选型决策的关键部分。本节展示了基于一个小型和一个中型项目的基准测试。以下是有关示例项目的一些说明:

  • 两组基准都不使用工作区功能
  • 小项目指定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

Performance results for Project 1






Method npm v8.1.2 Yarn Classic v1.23.0 pnpm v6.24.4 Yarn Berry PnP loose v3.1.1 Yarn Berry PnP strict v3.1.1 Yarn Berry node_modules v3.1.1 Yarn Berry pnpm v3.1.1
UC 1 86.63s 108.89s 43.58s 31.77s 30.13s 56.64s 60.91s
UC 2 41.54s 65.49s 26.43s 12.46s 12.66s 46.36s 40.74s
UC 3 23.59s 40.35s 20.32s 1.61s 1.36s 28.72s 31.89s
Files and size package-lock.json: 1.3M node_modules: 467M node_modules: 397M yarn.lock: 504K pnpm-lock.yaml: 412K node_modules: 319M yarn.lock: 540K cache: 68M unplugged: 29M .pnp.cjs: 1.6M yarn.lock: 540K cache: 68M unplugged: 29M .pnp.cjs: 1.5M node_modules: 395M yarn.lock: 540K cache: 68M node_modules: 374M yarn.lock: 540K cache: 68M
Performance results for Project 2






Method npm v8.1.2 Yarn Classic v1.23.0 pnpm v6.24.4 Yarn Berry PnP loose v3.1.1 Yarn Berry PnP strict v3.1.1 Yarn Berry node_modules v3.1.1 Yarn Berry pnpm v3.1.1
UC 1 34.91s 43.26s 15.6s 13.92s 6.44s 23.62s 20.09s
UC 2 7.92s 33.65s 8.86s 7.09s 5.63s 15.12s 14.93s
UC 3 5.09s 15.64s 4.73s 0.93s 0.79s 8.18s 6.02s
Files and size package-lock.json: 684K node_modules: 151M yarn.lock: 268K node_modules: 159M pnpm-lock.yaml: 212K node_modules: 141M .pnp.cjs: 1.1M .pnp.loader.mjs: 8.0K yarn.lock: 292K .yarn: 38M .pnp.cjs: 1.0M .pnp.loader.mjs: 8.0K yarn.lock: 292K .yarn: 38M yarn.lock: 292K node_modules: 164M cache: 34M yarn.lock: 292K node_modules: 156M cache: 34M

安全


npm

npm 在处理坏包时有点过于宽容并且遇到了一些直接影响许多项目的安全漏洞。例如,在 5.7.0 版本中,当您在 Linux 操作系统上执行 sudo npm 命令时,可以更改系统文件的所有权,从而导致操作系统无法使用。

另一起事件发生在 2018 年,涉及比特币被盗。 Node.js 包EventStream在其 3.3.6 版本中添加了恶意依赖项。这个恶意包包含一个加密方法试图从开发者的机器上窃取比特币。

为了帮助解决这些问题,新的 npm 版本使用密码算法来检查您安装的软件包的完整性。SHA-512

yarn

Yarn ClassicYarn Berry 从一开始就使用校验和来检验每一个包的完整性。Yarn 还试图阻止您检索在 package.json 中未声明的恶意包:如果发现不匹配,则中止安装。

PnP 模式下的 Yarn Berry 没有传统 node_modules 方式的安全问题。与 Yarn Classic 相比,Yarn Berry 提高了命令执行的安全性。您只能执行已在 package.json 声明的包。此安全功能类似于 pnpm,我将在下面进行描述。

pnpm

pnpm 还是使用校验和来验证每个已安装包的完整性,然后再执行其代码。

正如我们在上面提到的,npmYarn Classic 都存在由于提升而导致的安全问题pnpm 避免了这种情况,因为它的管理模型不使用提升;相反,它会生成嵌套 node_modules 文件夹,从而消除非法依赖访问的风险。这意味着依赖关系都在 .package.json 中声明了。

正如我们所讨论的,这在 monorepo 设置中尤其重要,因为提升算法有时会导致依赖的不确定性

热门项目使用情况






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

结论


不同的包管理器原理确实存在很大的差异。

pnpm 起初看起来像 npm,因为它们的 CLI 用法相似,但管理依赖项却大不相同;pnpm的方法带来更好的性能和最佳的磁盘空间效率。Yarn Classic 仍然很受欢迎,但它被认为是遗留软件,并且在不久的将来可能会放弃支持。Yarn Berry PnP 是全新的,但人们尚未意识到其再次彻底改变包管理器领域的潜力。

多年来,许多用户询问谁使用哪些包管理器,总体而言人们似乎对 Yarn Berry PnP 的成熟度和采用特别感兴趣。

本文的目的是为您提供多种观点,以决定您自己使用哪个包管理器。我想指出,我不推荐特定的包管理器。这取决于你如何衡量不同的要求——所以你仍然可以选择你喜欢的任何东西!

英文原文地址:https://blog.logrocket.com/javascript-package-managers-compared/

更多node相关知识,请访问:nodejs 教程

以上是JavaScript包管理器比较:Npm vs Yarn vs Pnpm的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文转载于:logrocket.com。如有侵权,请联系admin@php.cn删除