こんにちは、みんな!
さまざまなパッケージ マネージャーを使用しているNode.js プロジェクトを見たことがあると思います。たとえば、
私自身もそれを見てきましたし、上記のすべてに取り組んできましたが、常に疑問がありました。人々やチームがnpmではなくyarnやpnpmを使用する理由は何でしょうか? メリットは何でしょうか? デメリットはあるのでしょうか?
さて…調べてみましょう!
パフォーマンス比較ルール
npm 、 yarn、 pnpmを「速度」の観点から比較してみることにしました...
以下に 3 つの対策が表示されます。
キャッシュなしでロック ファイルを生成します。
キャッシュなしで既存のロック ファイルから依存関係をインストールします。
グローバル キャッシュを使用して既存のロック ファイルから依存関係をインストールします。
キャッシュには 2 つの種類があります。
グローバル。
通常、ユーザーのホームディレクトリ (fe、
~/.yarn/berry/cache
) に保存されます。
地元。
プロジェクト ディレクトリ (fe、
<project-dir>/.yarn
) に保存されます。
私の経験では、 #2と#3が最も一般的な使用例ですが、念のため#1も採用しました (ただし、非常にまれなケースです)。
ベンチマークの例として、create-react-appのサンプル プロジェクトを使用しました。
ネプ
これは、 Node.jsエコシステムのデフォルトのパッケージ マネージャーです。他に何を言うべきでしょうか。インストール パッケージに付属しているため、基本的には、マシンにNode.js をインストールするとすぐに使用できます (または、 CIプロバイダーでNode.jsをセットアップした場合は、そのプロバイダーにインストールします)。
これは私にとって大きな「利点」です。別途インストールする必要はありません。
特に目立つ点はありません。ただ…動作します。また、長年大きなバグは見ていません。かなり安定しており、問題なく動作します。
これまで使用したnpmの機能:
- 依存関係の管理(インストール、削除、更新)
- パッケージを公開する(プライベート、パブリック)
- リンクローカルパッケージ
- ワークスペースを管理します。
依存関係の管理
npm は依存関係をプロジェクト ルートのnode_modules
フォルダーに保存します。非常に簡単です。
ℹ️ package-lock.json
リストされているパッケージのレジストリに関する情報を保存します。単一のスコープ、つまり@example-company
からのパッケージを異なるレジストリ (例: npmとGitHub パッケージ) に持っている場合に非常に便利です。
さて、インストール速度の面でのパフォーマンスを見てみましょう...
キャッシュなしでpackage-lock.jsonを生成する
かかったpackage-lock.json
を生成し、キャッシュなしで依存関係をインストールします。
使用されたコマンド:
npm i
キャッシュなしでpackage-lock.jsonから依存関係をインストールする
かかったpackage-lock.json
から依存関係をインストールできるようにします。
使用されたコマンド:
npm ci
グローバルキャッシュを使用して package-lock.json から依存関係をインストールする
かかったpackage-lock.json
から依存関係をインストールします。
使用されたコマンド:
npm ci
ワークスペースの管理
ワークスペースを作成し、ワークスペース全体の依存関係を一度に管理したり、特定のプロジェクトの依存関係を個別に管理したりできるようになりました。
言い換えれば、バグや問題なく作業が完了し、公式ドキュメントも非常にわかりやすいということです。
これまで使用したワークスペース機能:
- ワークスペース内のすべてのプロジェクトの依存関係をインストールします。
- 特定の単一のプロジェクトの依存関係をインストールします。
- すべてのプロジェクトに対して単一のスクリプトを一度に再帰的に実行します。
糸
正直に言うと、私はyarn の機能のいくつかをあまり試していません。つまり、いくつかのプロジェクトで作業中に「依存関係のインストール」という点でそれをたくさん使用しましたが、それだけです。
yarnにはNode.jsインストーラーが付属していないため、別途インストールする必要があります。つまり、 CIパイプラインに追加の手順が必要になります。つまり、プロジェクトの依存関係をインストールする前にyarnを設定する必要があります。
依存関係の管理
yarnには依存関係をインストールする 2 つの方法があります。
「ゼロインストール」(デフォルト) -
.yarn
フォルダーを作成し、yarn.lock
および.pnp.cjs
ファイルにパッケージをリストします。
通常のものはnpmに似ており、依存関係を
node_modules
に保存し、yarn.lock
ファイルにリストします。
ℹ️ yarn lock ファイルには、古い (通常の) インストール方法を使用する場合にのみ、リストされているすべてのパッケージのレジストリに関する情報が保存されます。
⚠️ 「 Zero Installs 」はパッケージをローカル キャッシュに保存し、ロック ファイルへのリンクを提供しているようです。
依存関係を 1 つのクリーンな環境にインストールし、それを別の環境に移動するDockerfileまたはCIパイプラインがある場合、これは重要になる可能性があります ( .yarn
フォルダーとローカル キャッシュの両方をコピーする必要があります)。
現在、yarn のデフォルトのアプローチは「ゼロ インストール」であり、古いアプローチよりもパフォーマンスが優れているため、このアプローチのみでベンチマークを記録します。
キャッシュなしでロックファイルを生成する
かかったyarn.lock
ファイルを生成し、キャッシュなしで依存関係をインストールします。
使用されたコマンド:
yarn install
キャッシュなしで既存のロックファイルから依存関係をインストールする
かかった
使用されたコマンド:
yarn install --frozen-lockfile
グローバルキャッシュを使用して既存のロックファイルから依存関係をインストールする
かかった
使用されたコマンド:
yarn install --frozen-lockfile
ワークスペースの管理
ワークスペースを作成し、すべてのプロジェクトの依存関係を一度に管理したり、特定のプロジェクトの依存関係を個別に管理したりできるようになりました。
これまで使用したワークスペース機能:
- ワークスペース内のすべてのプロジェクトの依存関係をインストールします。
- 特定の単一のプロジェクトの依存関係をインストールします。
- すべてのプロジェクトに対して単一のスクリプトを一度に再帰的に実行します。
ドキュメントは問題ありませんが、コマンド名とフラグがややわかりにくいです。
たとえば、ルート( . ) およびネストされたb2b
プロジェクトでtest
スクリプトを実行するには、これを実行する必要があります。
yarn workspaces foreach -A --include '{.,b2b}' run test
npmとの比較:
npm run test --workspace=b2b --include-workspace-root
ピンポン
pnpmは現在話題になっており、多くの企業やオープンソース プロジェクトで使用されています。
yarnと同様に、 pnpmにはNode.jsインストーラーが付属していないため、別途インストールする必要があります。つまり、 CIパイプラインに追加の手順が必要になります。つまり、プロジェクトの依存関係をインストールする前にpnpmをセットアップする必要があります。
依存関係の管理
pnpm は「高速でディスク容量効率の良いパッケージ マネージャー」と考えられています...
確かに、依存関係をローカルで管理するという点では、 「ディスク スペース効率が良い」という意見に同意します。
デフォルトでは、 pnpm は共有依存関係の重複を排除します。pnpmは、複数の依存関係で使用されるパッケージのシンボリックリンクを作成します。つまり、パッケージa
とb
がパッケージc
を依存関係として使用する場合、 pnpm はパッケージc
単一のコピーとして保存し、パッケージa
とb
のシンボリックリンクを作成します。こうすることで、パッケージ マネージャーはハード コピーを作成せず、SSD/HDD のメモリを節約します。
ℹ️ pnpm-lock.yaml
リストされているパッケージのレジストリに関する情報は保存されません。
⚠️ pnpm は依存関係をプロジェクトとして保持するのではなく、グローバル キャッシュに保存することがあることに留意してください。
キャッシュなしでpnpm-lock.yamlを生成する
かかったpnpm-lock.yaml
を生成し、キャッシュなしで依存関係をインストールします。
使用されたコマンド:
pnpm install
グローバルキャッシュなしで pnpm-lock.yaml から依存関係をインストールする
かかったpnpm-lock.yaml
から依存関係をインストールします。
使用されたコマンド:
pnpm i --frozen-lockfile
グローバル キャッシュを使用して既存のロック ファイルから依存関係をインストールする
かかったpnpm-lock.yaml
から依存関係をインストールします。
使用されたコマンド:
pnpm i --frozen-lockfile
ワークスペースの管理
さて、ここからが本当に面白くなってくるのですが…
pnpmには多くの設定オプションがありますが、一部のコア機能は動作しません。
私が直面したバグをいくつか見てみましょう:
pnpm インストール --filter
特定のプロジェクトのみの依存関係をインストールできることは重要です。これは、ワークスペース内の特定のプロジェクトに関連するパイプラインを作成するときに、モノレポに非常に役立ちます。
つまり、ワークスペースに次のようなものがあると想像してください。
- ウェブアプリ、
- バックエンドサーバー、
- テスト プロジェクト (エンドツーエンド テスト)。
これらはすべて別々のnpmプロジェクトですが、同じリポジトリの一部です ☝️
ここで、パイプラインでエンドツーエンドのテストのみを実行する必要があります。つまり、エンドツーエンドのテスト依存関係のみが必要なわけですね。
まあ、それはできません - pnpm はワークスペース全体の依存関係をインストールするように強制します。
pnpm install --filter <project-name>
、選択したプロジェクトの依存関係のみをインストールするはずでしたが、機能しません。
1 年前のバグがあり、最近、機能しない修正で閉じられました。
再帰インストール=false
pnpmはデフォルトでpnpm install
実行するとワークスペース全体(すべてのプロジェクト)の依存関係をインストールします。
ワークスペース ルートの.npmrc
でrecursive-install=false
設定すると、この動作を切り替えることができます。
共有ワークスペースロックファイル=false
pnpm はデフォルトで依存関係リストを単一のロック ファイルに保存します ( npmおよびyarnと同じ)。
ワークスペース ルートの.npmrc
でshared-workspace-lockfile=false
を設定すると、この動作を変更することもできます。
これにより、ワークスペース機能を維持し、 --ignore-workspace
フラグを使用して特定のプロジェクトの依存関係をインストールできるようになります。
いずれにせよ、この設定により、さらにいくつかの問題が発生します。
eslint
とtsc --noEmit
GitHub Actionsパイプラインで「JavaScript ヒープ メモリ不足」エラーをスローします。
依存関係の一部はグローバル キャッシュに保存され、
node_modules/.pnpm
にシンボリック リンクされます。
パフォーマンス比較結果
# | ネプ | 糸 | ピンポン |
---|---|---|---|
ロックファイルを生成する | 60秒 | 16.5秒 | 31秒 |
キャッシュなしで依存関係をインストールする | 18秒 | 11秒 | 8秒 |
グローバルキャッシュを使用して依存関係をインストールする | 8秒 | 8秒 | 5秒 |
上記のベンチマークによると、 npm は最も遅いパッケージ マネージャーです ☝️
とにかく、これらの結果を解釈してみましょう...
ロックファイルを生成する
これはまれなケースです。通常、ロック ファイルはプロジェクトの初期化時に作成され、パッケージをインストール/更新するときに拡張されます。
それを念頭に置くと、パッケージ マネージャーを選択するときに頼るべきことはそれほど重要ではないようです。
依存関係をインストールする
ほとんどの場合、プロジェクトは特定の依存関係のリストを保持しており、何かを追加/削除することはほとんどありません。
おそらく、パッケージのバージョンは時々変更されるでしょう。これらの変更は小さく、残りのパッケージはキャッシュから再利用されます。
つまり、一般的な使用例は、パッケージ レジストリから新しいパッケージを取得し、残りをキャッシュから取得することです。
pnpm (5〜8 秒) はnpm (8〜18 秒) のほぼ 2 倍の速度で、 yarn (8〜11 秒) が中間です。
結論
事実
- pnpm は確かに「高速でディスク効率の良い」パッケージ マネージャーです。これは現在のレビューでも明らかです。
- pnpmワークスペース機能にはバグがあり、そのバグのいくつかは何年も解決されていません。
- pnpmとyarn はどちらも CI パイプラインで追加の設定が必要ですが、 npm では必要ありません。
- pnpmとyarn はどちらもロック ファイルにパッケージ レジストリ情報を保存しませんが、 npm は保存します。
著者の考え
パッケージ マネージャーに対する要件が「依存関係のみをインストールする」という単純なものである場合は、 pnpmが最適だと思います。
pnpmにはすぐに使えるNode.jsインストーラーが付属していませんが、 corepackまたは既存の actionを使用して CI パイプラインで簡単にセットアップできます。
私はnpmを好みます。その理由は次のとおりです:
- 安定している(特にワークスペース)
- Node.jsに付属しており、CIパイプラインで追加の設定は必要ありません。
- パッケージ レジストリを
package-lock.json
に保存するので、異なるレジストリから単一のスコープで依存関係をインストールできます。
これらの利点は、 yarnやpnpmで節約できる数秒の速度とディスク容量を上回ります。
パッケージ マネージャーを選択する際の基準は何ですか? 遠慮せずに、下のコメント セクションであなたの考えを教えてください! 👇😊