本篇文章帶大家了解三種JavaScript套件管理器(npm、yarn、pnpm),並將這三種套件管理器進行對比,聊聊npm、yarn、pnpm三者的差異和關聯,希望對大家有幫助,有問題歡迎指出!
套件管理器領域的三個主要參與者:
實際上我們已經在所有套件管理器中實現了基本相似的功能,因此您很可能會根據非功能性要求來決定使用哪個套件管理器,例如安裝速度、儲存消耗或實際情況。
當然,您選擇使用每個套件管理器的方式會有所不同,但它們都有基本一致的概念。以上這些套件管理器都可以執行以下指令:
然而儘管如此,套件管理器在底層還是有所不同的。傳統上 npm
和 Yarn
將依賴項安裝在一個平舖的node_modules資料夾中。 (這裡注意先後順序,是 yarn
先平舖的,之前 npm
是遞歸)。但是平鋪也會造成一連串的安全問題。
依賴結構的不確定性。
扁平化演算法本身的複雜性很高,耗時較長。
專案中仍然可以非法存取
#有宣告過依賴的套件
#因此,pnpm
在node_modules
資料夾中引入了一些新概念來更有效率的儲存依賴,。 Yarn Berry
甚至透過完全放棄 node_modules
的 (PnP) 模式(另一篇文章會具體說明)來走得更遠。
最早發布的套件管理器是 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
是「Node package manager」的首字母縮寫詞,但事實並非如此。
它的發布構成了一場革命,因為在此之前,專案依賴項都是手動下載和管理的。 npm
引入了諸如檔案及其元資料欄位、將依賴項儲存在node_modules
, 自訂腳本, 公用和私有套件等等。
2020 年,GitHub 收購了 npm,所以原則上 npm
現在歸微軟管理。在撰寫本文時,最新的主要版本是 v8,於 2021 年 10 月發布。
在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
的主要原因是 npm
和 Yarn
對於跨專案使用的依賴項儲存結構非常冗餘。儘管Yarn Classic
比npm
具有速度優勢,但它使用相同的依賴解析方法,這對pnpm
來說是不行的:npm
和Yarn Classic
使用hoisting
來平鋪他們的node_modules
.
pnpm
沒有最佳化先前的結構,而是引入了另一種依賴解決策略:內容尋址的一種儲存結構。此方法產生的 node_modules
資料夾其實是依賴全域儲存在主資料夾上的 ~/.pnpm-store/
目錄。每個版本的依賴項都以實體形式儲存在該目錄資料夾中一次,構成單一的來源位址來節省相當多的磁碟空間。
node_modules
結構是透過使用symlinks
建立依賴關係的嵌套結構(其中資料夾內每個檔案/套件都是透過硬連結儲存)官方文件中的下圖闡明了這一點。 (待填坑:軟硬連結)
2021 年報告中可見pnpm
的影響力:因為他們在內容可尋址儲存方面的創新,競爭對手都希望採用pnpm
的概念,例如象徵性連結的結構和套件的高效磁碟管理。
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
與 Node.js
一起提供,因此不需要額外的步驟。除了為您的作業系統下載Node.js 安裝程式外,使用 CLI 工具管理軟體版本已成為一種常見做法。在 Node 的上下文中,Node Version Manager (nvm) 或 Volta 已成為非常方便的實用程式。
您可以透過不同的方式安裝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 Classic
、Yarn Berry
和 pnpm
二进制文件所以您不必“单独”安装的 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
作为 npm
包来安装: $ npm i -g pnpm
。您还可以使用 Corepack 安装 pnpm :
$ corepack prepare pnpm@6.24.2 --activate
在本节中,您将一目了然地看到不同包管理器的主要特征。您可以轻松发现配置特定包管理器涉及哪些文件,以及哪些文件是由安装步骤生成的。
所有包管理器都将所有重要的元信息存储在项目清单package.json文件中。 此外,根级别的配置文件可以被用来设置不同的私有包或者不同的依赖项解析配置。
在安装步骤中,依赖项 dependencies
被存储在文件结构中,例如 node_modules
并生成锁定文件 locking
。本节不考虑工作区设置,因此所有示例仅显示存储依赖项的单个位置。
使用$ npm install
或较短的 $ npm i
会生成一个 package-lock.json
文件和一个 node_modules
文件夹。还有 .npmrc
这种可配置的文件可以放在根级别目录里面。有关 locking
文件的更多信息,请参阅下一节。
. ├── node_modules/ ├── .npmrc ├── package-lock.json └── package.json
运行 $ 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
项目中处理更多的文件和文件夹。有些是可选的,有些是强制性的。
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
无论是对于PnP 的严格模式还是松散模式,跟着 .pnp.cjs
和 yarn.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`
和 npm
或 Yarn 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
文件夹结构中。这是 npm
、Yarn Classic 和 pnpm都使用的方案,(其中 pnpm
比其他方案更有效)。
Yarn Berry
在 PnP 模式下的做法有所不同。依赖项不是 node_modules
文件夹,而是以 zip 文件的形式存储为 .yarn/cache/
和 .pnp.cjs
文件的组合。
最好将这些锁定文件置于版本控制之下,因为每个团队成员都安装相同的版本,所以它解决了“在你和我的机器上工作”问题。
下表比较了 npm
、Yarn Classic
、Yarn Berry
和 pnpm
中可用的不同 CLI 命令。这绝不是一个完整的列表,而是一个备忘单。本节不涉及与workflow 相关的命令。
npm
和 pnpm
具有许多特别的命令和选项别名,这意味着命令可以有不同的名称,即$ npm install
与 $ npm add
。 此外,许多命令选项都有缩写版本,例如 -D
来代替 --save-dev
。
在表格中,我将所有缩写版本称为别名。使用这些包管理器,您都可以增加、更新或删除多个依赖项。
此表涵盖了用于安装或更新 package.json
中指定的所有依赖项的依赖项管理命令。
動作 | npm | Yarn Classic | Yarn Berry | pnpm |
---|---|---|---|---|
在package.json中安裝deps | npm install別名:i,add | yarn install或yarn | #like Classic | pnpm install 別名:i |
更新 package.json 中的 deps acc。 semver | npm 更新別名:up、upgrade | yarn 升級 | yarn semver up(透過 plugin) | pnpm 更新別名: up |
將package.json 中的deps 更新為最新 | N/A | yarn 升級--latest | #yarn up | pnpm update --latest 別名:-L |
#update deps acc。 semver | npm 更新react | yarn 升級react | yarn semver 更新react | pnpm 更新react |
#將deps 更新至最新 | npm update react@latest | yarn升級react --latest | yarn up React | pnpm up -L React |
N/A | yarn 升級互動 | yarn 升級互動(透過 | plugin) | $ pnpm up --interactive 別名:-i|
npm i React | ##yarn add React | 像經典 | pnpm add React | |
npm i -D babel 別名:--save-dev | yarn add -D babel 別名:--dev | like Classic | pnpm add -D babel 別名:--save-dev | |
npm i -E React 別名:--save-exact | yarn add -E React 別名:--exact | like Classic | pnpm add -E React alias: --save-exact | |
npm uninstall React別名:刪除,rm,r,un,unlink | yarn刪除反應 | 像經典 | pnpm刪除反應別名:rm,un,uninstall | |
npm uninstall --no-save | N/A | N/A | N/A |
套件:依賴或二進位Yarn Berry是要理解的,
- 二進位:一種執行工具來自
node_modules/.bin/- 或
. yarn/cache /
(PnP)
只允許我們在 package.json
中執行在bin/
檔案中指定的二進位檔案。
npm | Yarn Classic | Yarn Berry | pnpm | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
npm i -g ntl alias: --global | yarn 全域新增ntl | N/A( | 全域已刪除)pnpm add --global ntl | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
npm update -g ntl | yarn全域升級ntl | N/A | pnpm update --global ntl | ##全域刪除套件 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yarn 全域刪除ntl | N/A | pnpm remove - - global ntl | 從終端機執行二進位檔案 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yarn ntl | yarn ntl | pnpm ntl | 從腳本執行二進位檔案 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ntl | ntl | ntl | 動態套件執行 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
N/A | yarn dlx ntl | pnpm dlx ntl | ##新增執行階段依賴 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yarn add react | 像經典 | pnpm add react | ##add dev deps | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yarn add -D babel 別名:--dev | 像Classic | pnpm add -D babel alias: --save-dev | 加入deps到package.json而不帶semver | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yarn add -E 反應別名:--exact | 像經典 | pnpm add -E 反應別名:- -save-exact | 卸載deps 並從package.json 中刪除 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yarn remove react | 像經典 | pnpm刪除react別名:rm,un,uninstall | 卸載deps而不更新package.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
N/A | N/A | N/A | ## |
Action | npm | Yarn Classic | Yarn Berry | |
---|---|---|---|---|
npm publish | yarn publish | yarn npm publish | #pnpm publish | |
npm ls alias: list, la, ll | yarn list |
pnpm list alias: ls |
||
npm outdated | yarn outdated | #yarn upgrade-interactive | pnpm outdated | |
npm explain ntl alias: why | yarn why ntl | like Classic | pnpm why ntl | |
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 | |
N/A (via third-party package) | yarn licenses list | N/A (or via | plugin, other plugin)N/A (via third-party package) | |
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 | ##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
中的工作区大多数配置发生在专用配置文件 .npmrc
中。
如果你想使用 npm
的 workspaces
功能,你必须在 package.json
中添加workspaces 元数据字段来告诉 npm 在哪里可以找到子项目或工作空间的文件夹。
// ... "workspaces": [ "hooks", "utils" ] }
每个包管理器都可以使用公共 npm
注册表。或许你很可能希望重用它们而不将它们发布到公共注册表。您可以在 .npmrc
文件中执行此操作配置来私有注册表。( 现在基本都有私有源了)
# .npmrc @doppelmutzi:registry=https://gitlab.doppelmutzi.com/api/v4/projects/41/packages/npm/
npm
存在许多配置选项,最好在文档中查看它们。\
您可以在 package.json
中设置 yarn
的 workspaces
(必须是私有包)。
{ // ... "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
中配置 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></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
使用与 npm
相同的配置机制,因此您可以使用 .npmrc
文件。配置私有注册表的工作方式也与使用 npm
相同。借助 pnpm 的工作空间功能可以支持多包项目。要初始化 monorepo
,您必须在 pnpm-workspace.yaml
文件中指定包的位置。
# pnpm-workspace.yaml packages: - 'packages/**'
(这里其实就是三种概念,单仓库多项目,单仓库单项目,多仓库多项目)
monorepo
是一个包含多个项目的存储库,这些项目被称为 workspace
或者包。将所有内容保存在一个地方而不是使用多个存储库是一种项目组织策略。
当然,这会带来额外的复杂性。Yarn Classic
是第一个启用此功能的,但现在每个主要的包管理器都提供了工作区功能。本节展示如何使用每个不同的包管理器配置工作区。
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 目前不支持高级过滤或并行执行多个与工作区相关的命令。
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 Classic
的概念之上的。在Reddit 评论中,Yarn Berry 的主要开发人员简要概述了面向工作空间的功能,包括:
Yarn Berry
使用大量可用于 package.json
文件的 dependencies
或 devDependencies
字段的协议。其中就有 workspace
协议。
与 Yarn Classic
的工作区相比,Yarn Berry
明确定义依赖项必须是此 monorepo
中的包之一。否则如果版本不匹配,Yarn Berry
可能会尝试从远程注册表获取其版本。
{ // ... "dependencies": { "@doppelmutzi/hooks": "workspace:*", "http-server": "14.0.0", // ... } }
通过 workspace
这种协议,pnpm
促成了类似于 Yarn Berry
的 monorepo
项目。许多 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/`
性能是选型决策的关键部分。本节展示了基于一个小型和一个中型项目的基准测试。以下是有关示例项目的一些说明:
我用三个用例 (UC) 对我们的每个包管理器变体进行了一次测量。要了解详细的评估和解释,请查看项目 1 (P1)和项目 2 (P2)的结果。
node_modules
或 .pnp.cjs
node_modules
或 .pnp.cjs
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
在處理壞套件時有點過於寬容並且遇到了一些直接影響許多專案的安全漏洞。例如,在 5.7.0 版本中,當您在 Linux 作業系統上執行 sudo npm
命令時,可以更改系統檔案的所有權,從而導致作業系統無法使用。
另一起事件發生在 2018 年,涉及比特幣被盜。 Node.js 套件EventStream在其 3.3.6 版本中新增了惡意依賴項。這個惡意包包含一個加密方法試圖從開發者的機器上竊取比特幣。
為了幫助解決這些問題,新的 npm
版本使用密碼演算法來檢查您安裝的軟體包的完整性。 SHA-512。
Yarn Classic
和Yarn Berry
從一開始就使用校驗和來檢驗每一個包的完整性。 Yarn
也試圖阻止您檢索在 package.json
中未聲明的惡意套件:如果發現不匹配,則中止安裝。
PnP 模式下的 Yarn Berry
沒有傳統 node_modules
方式的安全問題。與 Yarn Classic
相比,Yarn Berry
提高了指令執行的安全性。您只能執行已在 package.json
宣告的套件。此安全功能類似於 pnpm
,我將在下面進行描述。
pnpm
或使用校驗和來驗證每個已安裝套件的完整性,然後再執行其程式碼。
如我們在上面提到的,npm
和 Yarn Classic
都存在著由於提升而導致的安全性問題。 pnpm
避免了這種情況,因為它的管理模型不使用提升;相反,它會產生巢狀 node_modules
資料夾,從而消除非法依賴存取的風險。這意味著依賴關係都在 .package.json
中宣告了。
正如我們所討論的,這在 monorepo
設定中尤其重要,因為提升演算法有時會導致依賴的不確定性。
|
|||
---|---|---|---|
##npm | Yarn Classic | Yarn Berry | |
##Svelte | #React | Jest | |
# (與node_modules) | ##Vue 3 | Preact | |
Angular | ##故事書#(帶有node_modules) | #瀏覽器清單Express. js | |
Babel | #(帶有node_modules) | Prisma||
Next.js |
Redux 工具包 | (帶有node_modules)
| SvelteKit|
Apollo 伺服器 |
Gatsby | ||
不同的套件管理器原理確實存在很大的差異。
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中文網其他相關文章!