【フロントエンドエンジニアリング】パッケージマネージャーの開発

少し前に突然 tnpm に出会い、npm install の最適化の原理を学びました。ビジネスには役に立ちませんが、最適化のアイデアを学ぶことは非常に有益です。学習の過程で、最近のnpm、yarn、pnpmの長所と短所を簡単にまとめたいと思います。

  • 最初は URL を使用してコードを共有します。たとえば、JQuery を使用したい場合は、JQuery 公式 Web サイトにアクセスしてそのダウンロード リンクを使用します。欠点は、他の人のオープン ソース コードを使用するたびに、対応するダウンロード リンクを 1 つずつ見つけてファイルにインポートする必要があることです。
  • Npm は、管理のためにこれらのオープン ソース コードを収集するツールを使用し、必要に応じてそれらをプロジェクトにインポートするコマンドを実行するために誕生しました。
  • node.jsが誕生してからパッケージ管理ツールが急務となり、npmと意気投合してnpmが普及しました~。

npm

npm インストールの原則

ここに画像の説明を挿入

  • プロジェクト レベルの.npmrcファイル > ユーザー レベルの.npmrcファイル > グローバル レベル.npmrc>npm組み込み.npmrcファイルから構成を取得します。
  • ファイルを確認しますpackage-lock.json
    • その場合は、依存関係が宣言と一致している package-lock.jsonことを確認してください。package.json
      • 一貫して、依存関係を処理するために直接使用しますpackage.json
      • 不一致は、バージョンごとに異なる処理が行われます。
    • 存在しない場合は、
      • 依存関係ツリー関係の再帰的構築によるとpackage.json、構築プロセスは次のとおりです。
        • 直接の依存関係であるか副依存関係であるかに関係なく、node_modules のルート ディレクトリに置くことをお勧めします。
        • 同じモジュールが見つかった場合は、依存関係ツリーに配置されているモジュールのバージョンが新しいモジュールのバージョン範囲を満たしているかどうかを判断し、満たしている場合はスキップし、そうでない場合は、現在のモジュールの node_modules の下にモジュールを配置します。
      • 依存関係ツリー内の各パッケージをキャッシュから順番に検索して、キャッシュがあるかどうかを判断します。
        • キャッシュが存在しません:
          • リモートリポジトリからnpmダウンロード
          • パッケージの整合性を確認します。
            • 失敗した場合は、再ダウンロードしてください。
            • 検証に合格しました:
              • ダウンロードした圧縮パッケージをキャッシュ ディレクトリにコピーします。
              • ダウンロードしたパッケージを次のコマンドを使用してpacote解凍します。node_modules
        • キャッシュがあり、キャッシュされたパッケージはpacote次のように解凍されます。node_modules
      • キャッシュされたパッケージを次の助けを借りpacoteて解凍します。node_modules
      • ファイルを生成しますpackage-lock.json

フラット構造

資源の無駄遣い

前述したように、npm3将来的にはフラット構造が採用される予定ですが、この構造にもデメリットがあります。

node_modules
└──A
    └──node_modules
      └──B V1.0
└──C
    └──node_modules
      └──B V2.0
└──D
    └──node_modules
      └──B V2.0
└──E

A前述のインストールのメカニズムから、依存パッケージは内部的に依存しているためB V1.0A依存パッケージはB V1.0ルート ディレクトリにインストールされることは推測できますが、依存パッケージをインストールする場合C、ルート ディレクトリは既に存在するためB V2.0同じ理由でルートディレクトリB V2.0にインストールされます。つまり、構造はおおよそ次のとおりです。Cnode_modulesDnode_modules

node_modules
├──A
├──B V1.0
└──C
    └──node_modules
      └──B V2.0
└──D
    └──node_modules
      └──B V2.0
└──E

ここに画像の説明を挿入
ここで、バージョンが繰り返され、リソーススペースが無駄にされていることがわかります。

不確実性非決定論

依存関係があると、同じpackage.jsonファイルがinstall 同じnode_modules ディレクトリ構造にならない場合があります。

前の例でも、A は [email protected] に依存し、C は [email protected] に依存し、インストール後に B の 1.0 と 2.0 のどちらをアップグレードする必要があるかが異なります。

node_modules
├── [email protected]
├── [email protected]
└── [email protected]
    └── node_modules
        └── [email protected]
└── [email protected]
    └── node_modules
        └── [email protected]

node_modules
├── [email protected]
│   └── node_modules
│       └── [email protected]
├── [email protected]
└── [email protected]
└── [email protected]

ユーザーのインストール順序によって異なります。

重複モジュール:同じ名前とsemver (セマンティック バージョン) 互換性を持つモジュールを指します各モジュールはバージョン許容範囲に対応しており、2 つのモジュールのバージョン許容範囲が重複する場合、互換性のあるバージョンを取得できます。

ファントム依存関係

依存関係が宣言されていないパッケージは、不正にアクセスされる可能性があります。dependenciesモジュールを直接記述することはありませんがB、直接行うことはできrequire('B');ますphantom dependencyライブラリがリリースされると、Bユーザーがライブラリをインストールするときにモジュールがインストールされないため、エラーが報告されます。

時間がかかる

平坦化アルゴリズム自体は非常に複雑で、時間がかかります。

Yarn最適化されたいくつかの問題npm3: 依存関係のインストールの遅さと不確実性。

インストール速度の向上

npm 依存関係をインストールする場合、インストール タスクはシリアル化され、インストールはパッケージの順序で 1 つずつ実行されます。つまり、パッケージが完全にインストールされるのを待ってから次のパッケージに進みます。

パッケージのインストールを高速化するために、yarn並列操作が採用され、パフォーマンスが大幅に向上しました。また、キャッシュ機構によりyarn 各パッケージはディスク上にキャッシュされ、次回パッケージをインストールする際には、ネットワークを介さずにオフラインでディスクからインストールすることができます。

ロックファイルは不確実性を解決します

依存関係のインストール中に、package.josn に従ってyarn.lock ファイルが生成されます。このファイルには、依存関係、依存するサブ依存関係、依存するバージョン、アドレスを取得してモジュールの整合性を検証するためのハッシュが記録されます。インストール順序が異なる場合でも、同じ依存関係はどの環境やコンテナーでも安定した node_modules ディレクトリ構造を取得できるため、依存関係のインストールの決定性が保証されます。

デメリット

ゴースト依存とクローン依存の問題はいまだ解決されていない。

Npm はその後、不確実性の問題を解決するために package-lock.json も使用しました。キャッシュ戦略も V5 バージョン後に追加されたため、npm のアップグレードでは、yarn の多くの利点が明白ではなくなりました。

CNPM

関連するソース ファイルのダウンロードを高速化します。原則として、cnpm私たちが行うことは、すべての人のためにそれを変更することですregistry

淘宝源は今年6月にアドレスを変更したため、cnpmを使用する場合は最新のアドレスに更新または置き換える必要がありますregistry

アドレス:元の Taobao npm ドメイン名はまもなく解析を停止します

ヤーンベリー

溝のノードモジュール

npm または にかかわらずyarn、キャッシュの機能があり、依存関係をインストールするとき、実際にはキャッシュ内の関連パッケージをプロジェクト ディレクトリnode_modules に。IO 操作に関しては、非常に時間がかかります。

このステップはコピーされませんがyarn PnP、プロジェクト内に静的なマッピング テーブルが維持されますpnp.cjs

pnp.cjs キャッシュ内の依存関係の特定の場所が記録され、すべての依存関係がグローバル キャッシュに保存されます。同時に、ノードが参照に依存する場合に検索するのではなく、グローバル キャッシュ ディレクトリから依存関係を見つけられるようにするために、独自に構築されたパーサーが構築されていますnode_modules

これにより、大量の I/O 操作が回避され、プロジェクト ディレクトリが生成されなくなり、node_modules 同じバージョンの依存関係のコピーがグローバルに 1 つだけになり、インストール速度と依存関係の解析速度が大幅に向上します。

ノードエコロジーからの脱却

  • PnP を使用するため、 はなくnode_modules なりますが、Webpack,Babelなどのさまざまなフロントエンド ツールが存在しますnode_modulesなどの多くのツールはpnp-webpack-plugin解決されていますが、互換性のリスクは避けられません。
  • PnP は独自の依存関係パーサーを構築しており、すべての依存関係参照はパーサーによって実行される必要があるため、ノード スクリプトは Yarn コマンドを通じてのみ実行できます。

PNPM

アドバンテージ

前述したように、npm3フラット構造を採用しているため、次のような問題があります。

  • ディスクリソースの使用量が多い。
  • ファントム依存関係。依存関係が宣言されていないパッケージに不正にアクセスする可能性があります。
  • 平坦化アルゴリズムは複雑で時間がかかります。

そして、pnpm上記の問題はすべて改善されました。

pnpm i express

node_modulesフォルダーを確認します。

.pnpm
.modules.yaml
express

これはexpressソフト リンクです。内部にはディレクトリはなくnode_modules、実際のファイルの場所は.pnpm-storeフォルダー内にあります。

▾ node_modules
  ▾ .pnpm
    ▸ [email protected][email protected]
    ...
    ▾ [email protected]
      ▾ node_modules
        ▸ accepts  -> ../[email protected]/node_modules/accepts
        ▸ array-flatten -> ../[email protected]/node_modules/array-flatten
        ...
        ▾ express
          ▸ lib
            History.md
            index.js
            LICENSE
            package.json
            Readme.md

この結果はpackage.json 基本的に宣言された依存関係と一致し、リソースの使用量も削減されます。この依存関係メソッドの管理により、依存関係の昇格のセキュリティ問題も解決されます。

デメリット

プロジェクトの分離

すべてがハード リンクを通じて同じソース コード ファイルにリンクされているため .pnpm、特定のプロジェクトでこのパッケージのファイルを変更すると、そのパッケージはすべてのプロジェクトで変更されるため、変更されたプロジェクトを分離することができなくなります。

依存関係を修正する

フロントエンド開発の過程では、サードパーティのオープンソースライブラリのバグに遭遇することがよくありますが、通常は次のような方法で対処します。

  • ソース コードの変更のコピーを自分でフォークし、修復後、ローカルにパッケージ化して直接使用できます。研究結果を他の人と共有したい場合は、npm リポジトリにアップロードするか、ソース リポジトリに PR を送信します。この方法には欠点があります。それは、ノートを公式ライブラリと同期させるのが難しいということです。
  • ライブラリ作成者が修正するのを待っています。オープンソース作成者は一般に多忙であり、ユーザーのニーズが最前線にない可能性があるため、この方法は信頼できません。
  • pacth-packageローカルパッケージにパッチを適用するnpm

ただし、pacth-packagepnpm はサポートされていません。

npmの最適化の方向性

Ant Group の npm エンジニアである Lingyi は、 SEE Con​​f 2022 Alipay Experience Technology Conference : tnpm (Ant Group Luban Award) でnpm をインストールする第 2 レベルの方法を共有し、npm インストールの問題点と最適化ソリューションを提案しました。

HTTPリクエスト

ここに画像の説明を挿入

キャッシュに関係なく、実行中にnpm install、現在の依存関係のパッケージ情報を順次かつ再帰的に取得し、tarそれに応じてダウンロードします。つまり、HTTP リクエストの数が増加し、依存関係ツリーの生成時間が徐々に増加します。

この点において、HTTP リクエストの量は集約によって削減できます

ここに画像の説明を挿入

プロジェクトをpackage.jsonサーバーに送信し、サーバー上で実行して@npmcli/arborist 依存関係ツリーを生成します。arboristアクセスされたregistry インターフェースHTTP を当社のサービスに直接ハイジャックしますregistryメモリキャッシュ/分散キャッシュ/DBにより依存関係ツリーの生成処理を高速化します。

@npmcli/arboristこれは、npm の低レベルの検査および管理ツリーのパッケージですnode_modules

I/O操作

ここに画像の説明を挿入

この記事では、例としてインストールしたパッケージを使用しています。tar パッケージをローカルにプルした後、解凍後に必要な IO 操作には、フォルダーの作成、ファイルの書き込み、bin ファイルへのソフト リンク、bin の読み取りおよび書き込み権限の設定が含まれます。つまり、合計 13 の IO 操作が関係します。

依存パッケージに対応するパッケージnpm installはその時点でウェアハウスからプルされtar、tar パッケージはキャッシュにも使用されるため、最適化方法は最初から始まりますtartarパッケージを解凍する必要がない場合は、上記の IO 操作は必要ありません。

ここに画像の説明を挿入

tar 最後にファイルを追加するのは非常に簡単です。したがって、この 2 つをマージできますtar

ここに画像の説明を挿入

両方のパッケージを作成するフローは次のようになります。

  1. fs.createFile: 公開ファイルを作成する
  2. fs.appendFile: 最初のパッケージを書き込む
  3. fs.appendFile: 2 番目のパッケージを書き込む

26 IO から 3 IO 操作に減少しました。

ダウンロードとインストールは高速になりましたが、インストールしたものは使用できません。tar これはファイルではありませんJS 。直接JS 使用しrequireshell ファイル内で操作したり、IDE で編集したりすることはできません。すべての習慣が壊れます。tar 次に、読み取りと書き込みの問題を解決する必要があります。

requireJS を通過するプロセスを学習します。

  1. この APIを使用してfs.readFile、ファイル読み取りリクエストを開始します。
  2. JS メソッドはデータ結果をlibuv次のように構築します。uv_req_t
  3. libuv libc のメソッドが呼び出されますread
  4. readこのメソッドは、カーネル内のファイル システムにアクセスしてファイルを読み取るシステム コールを開始します。

ここに画像の説明を挿入

PnP はzip パッケージの形式で保存されます。パッケージの読み込みはハイジャックnode というrequire方法で実現しておりzip、読み込みIDE はプラグインの開発によってサポートされていますIDE ただし、コミュニティにはディレクトリをfsAPI横断するための実装が多数あり、開発者はこれを使用して一部の依存関係操作を実行することもあります。既存の使用習慣の場合、より大きなダメージを与えます。node_modulesshell

ここに画像の説明を挿入

  • tar ファイルの読み取りを解決します。FUSE (FUSE の正式名は FileSystem In UserSpace で、SSH などの特定のネットワーク スペースをローカル ファイル システムにマウントするために Linux で使用されるモジュールです) を使用して、ユーザー モード プログラムでファイル システムを実装します。

ここに画像の説明を挿入

  • tar ファイルの変更を解決する: オーバーレイ ファイル システム (オーバーレイ ファイル システムは Docker コンテナーで広く使用されているファイル システムであり、書き込み時にコピーするという考え方は、ファイルを下位と上位の 2 つの層に分割することです) を使用して、下位ディレクトリは読み取り専用で、上位ディレクトリは読み取りおよび書き込み可能です。オーバーレイで上位ディレクトリと下位ディレクトリを結合して、読み取りおよび書き込み可能なディレクトリを構築できます。ファイルへの変更は、下位ディレクトリには影響せずに、上位ディレクトリに反映されます。プロジェクトの分離が解決されました

ここに画像の説明を挿入

キャッシュ

インストール速度を解決したら、最後のディスク容量の問題を解決しなければなりませんが、npm はインストール後に多くの容量を占有し、ブラック ホールの名にふさわしいものになっています。

NPM はグローバルtarキャッシュを使用してダウンロード プロセスを高速化し、繰り返しのダウンロードを減らします。しかし、毎回解凍するのに時間がかかりすぎます。

pnpm ファイルのハードリンクという形式は書き込み量を減らすために使用されますが、ハードリンクは全体が同じファイルを指すことを意味し、例えば、2つのプロジェクトが同じパッケージに依存している場合、一方のプロジェクトが何らかの変更をdebug 加える

オーバーレイのもう 1 つの機能は、基礎となるファイルを変更するときに、基礎となるファイルを上位ディレクトリにコピーする COW (Copy On Write) です。したがって、同じキャッシュを使用してすべてのプロジェクトをグローバルにサポートできます。

ここに画像の説明を挿入

他の

Corepack「パッケージマネージャーを管理するマネージャー」

Corepack は、Node.js v16.13 リリースで導入された実験的なツールです。

  • Yarn pnpm などのツールをグローバルにインストールする必要はなくなりました。
  • 更新が必要になるたびに手動で同期する必要がなく、チーム プロジェクトに特定のパッケージ マネージャー バージョンの使用を強制することができ、構成と一致しない場合はコンソールでエラーが表示されます。

package.json で設定できます。

"packageManager": "[email protected]"
// 声明的包管理器,会自动下载对应的 yarn,然后执行
yarn install

// 用非声明的包管理器,会自动拦截报错
pnpm install
Usage Error: This project is configured to use yarn

テスト段階での問題:

  • 現在、pnpm と Yarn のみがサポートされており、cnpm はサポートされていません。
  • 互換性に関してはまだいくつかの問題があり、npm はそれをインターセプトできません。つまり、packageManager が糸を使用するように構成されている場合でも、グローバル npm インストールを呼び出すことができます。

@antfu/ni

実行する前にyarn.lock// を検出して現在のパッケージ マネージャーを確認し、適切なコマンドを実行します。pnpm-lock.yamlpackage-lock.json

使用 `ni` 在项目中安装依赖时:
   假设你的项目中有锁文件 `yarn.lock`,那么它最终会执行 `yarn install` 命令。
   假设你的项目中有锁文件 `pnpm-lock.yaml`,那么它最终会执行 `pnpm i` 命令。
   假设你的项目中有锁文件 `package-lock.json`,那么它最终会执行 `npm i` 命令。

npm i -g @antfu/ni
ni

参考


間違いがあればご指摘ください、読んでいただきありがとうございます~

おすすめ

転載: blog.csdn.net/qq_34086980/article/details/125837412