こんにちは、みんな!
さまざまなパッケージ マネージャーを使用している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
を生成し、キャッシュなしで依存関係をインストールします。
使用されたコマンド:
npm i
かかったpackage-lock.json
から依存関係をインストールできるようにします。
使用されたコマンド:
npm ci
かかった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 install
かかったpnpm-lock.yaml
から依存関係をインストールします。
使用されたコマンド:
pnpm i --frozen-lockfile
かかったpnpm-lock.yaml
から依存関係をインストールします。
使用されたコマンド:
pnpm i --frozen-lockfile
さて、ここからが本当に面白くなってくるのですが…
pnpmには多くの設定オプションがありますが、一部のコア機能は動作しません。
私が直面したバグをいくつか見てみましょう:
特定のプロジェクトのみの依存関係をインストールできることは重要です。これは、ワークスペース内の特定のプロジェクトに関連するパイプラインを作成するときに、モノレポに非常に役立ちます。
つまり、ワークスペースに次のようなものがあると想像してください。
これらはすべて別々のnpmプロジェクトですが、同じリポジトリの一部です ☝️
ここで、パイプラインでエンドツーエンドのテストのみを実行する必要があります。つまり、エンドツーエンドのテスト依存関係のみが必要なわけですね。
まあ、それはできません - pnpm はワークスペース全体の依存関係をインストールするように強制します。
pnpm install --filter <project-name>
、選択したプロジェクトの依存関係のみをインストールするはずでしたが、機能しません。
1 年前のバグがあり、最近、機能しない修正で閉じられました。
pnpmはデフォルトでpnpm install
実行するとワークスペース全体(すべてのプロジェクト)の依存関係をインストールします。
ワークスペース ルートの.npmrc
でrecursive-install=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にはすぐに使えるNode.jsインストーラーが付属していませんが、 corepackまたは既存の actionを使用して CI パイプラインで簡単にセットアップできます。
私はnpmを好みます。その理由は次のとおりです:
package-lock.json
に保存するので、異なるレジストリから単一のスコープで依存関係をインストールできます。
これらの利点は、 yarnやpnpmで節約できる数秒の速度とディスク容量を上回ります。
パッケージ マネージャーを選択する際の基準は何ですか? 遠慮せずに、下のコメント セクションであなたの考えを教えてください! 👇😊