Nodejs アプリケーションのコンパイルと構築を高速化するための提案 | JD Cloud テクニカル チーム

コンパイルとビルドの全体的なプロセス

  1. コンパイルされたイメージをプルします

  2. キャッシュイメージをプルする

  3. プロジェクトのソースコードをプルする

  4. キャッシュディレクトリをマウントする

  5. コンパイルコマンドの実行(ユーザー定義)

  6. 永続的なキャッシュ

  7. コンパイルした画像をアップロードする

ローカルでのビルドは速いのに、コンパイラでは非常に遅いのはなぜですか

編集マシン上の各ビルド環境はまったく新しいもので、ビルドを完了するにはローカルの場合よりもいくつかの手順が必要です。

  1. 既製のグローバルパッケージキャッシュ VS 再構築キャッシュ: npm を使用すると、単純にグローバル キャッシュ ディレクトリとして理解できますが、エディタはキャッシュのダウンロード、マウント、再構築を含む永続的なキャッシュ環境を準備する必要があります。サイズが大きすぎると、時間が比較的長くなり、ローカル ビルドでは安定したローカル ファイル システムが直接使用されます。

  2. 増分インストールの依存関係 VS 完全インストールの依存関係: インストールのプロセスは、ローカルで必要になることはあまりありません。必要な場合でも、永続的な node_modules ディレクトリがあるため、完全インストールは必要ありませんが、エディター環境では、このプロジェクトに必要なものすべてを再インストールする必要があります。毎回。

  3. インクリメンタル ビルド VS フル ビルド: ローカル ビルドでは、デフォルトでビルド キャッシュが node_modules ディレクトリに配置され、これらのビルドは 2 番目のビルド中に使用できるため、後続のビルドが高速になりますが、このビルドのデフォルトのキャッシュの場所は編集されません。つまり、毎回完全にビルドする必要があります。

  4. ネットワーク環境: 一部の依存パッケージのインストールは外部ネットワークや海外ネットワークに依存する場合があり、ローカルネットワーク環境は比較的スムーズですが、編集機のネットワークは海外ネットワークへのアクセスを保証するものではありません。

  5. 使いにくい利点: マルチコアと大容量メモリ、nodejs プロジェクトの構築における作業のほとんどが 1 つのスレッドで実行される、コンパイラのマルチコアの利点を直接使用するのは簡単ではない

  6. 追加の手順: コンパイラーはイメージをダウンロードし、実行イメージを作成してアップロードし、キャッシュされたコンテンツを保持する必要がありますが、ローカルは通常、出力パッケージのみです。

したがって、上記の観点から始めて、次のようないくつかのアイデアに基づいて建設速度を最適化できます。

  1. 画像サイズを最適化します。

  2. 永続キャッシュを有効に活用して増分ビルドを実現します (/cache/ ディレクトリの内容を永続的にキャッシュする編集機会)

  3. マルチコアの利点を最大限に活用します。

    たとえば、ts-loader の型検証は他のプラグインを介して別のスレッドで実行できます。また、eslint-loader はマルチスレッドもサポートしています (ただし、現時点ではバグがあるため推奨されません)。

    別の例として、プロジェクトの機能モジュールを分離し、同時に複数のビルドに分割することができます。

  4. 不必要なビルドを削減します。

    たとえば、ビルド ファイルのスコープを合理化するために除外を合理的に構成します。

    頻繁に変更しないファイルの場合は、ファイルを取り出して一度ビルドし、次回再利用します。

  5. 外部ネットワークに依存するパッケージを削除する他の方法があるかどうかを判断する

ビルド速度を分析する方法

  1. /cache/ ディレクトリのサイズを確認します。
  2. du -sh /cacheビルド ログを通じてディレクトリ サイズを表示するには、コンパイル コマンドに次を追加します。
  3. 全体的なコンパイルコマンドの前後に追加するとdate、自分のプロジェクトの時間のかかる構築過程、つまりコンパイルコマンドの実行時間がわかります
  4. メインのコンパイル コマンドの各行の前に追加しますtime。例:time npm installインストール プロセスの実際の消費時間を確認できますが、ビルド プロセスは同じです。
  5. 全体のビルド時間 (Web ページに直接表示されるタスク時間) とコンパイル コマンドの実行時間 (終了時の日付時刻 - 開始時の日付時刻) を比較します。全体の時間がコンパイル コマンドの実行時間を大幅に超えている場合 (1 分 30 秒以上) )、/cache/ ディレクトリであるか、画像が大きすぎる可能性があります。

詳細な紹介は次のとおりです。

小さい実行イメージを使用する

それより大きな画像がある場合は、運用保守に連絡して最適化することをお勧めします。

永続キャッシュを上手に活用する

キャッシュによりアプリケーションの構築を高速化できますが、キャッシュ ディレクトリが増大し続けると、速度がある程度低下する可能性があります。

キャッシュのメカニズムを理解します。

1. 缓存目录: /cache/

2. 默认行为: 对于 nodejs 的应用, 目前持久缓存会为 npm, pnpm 提供安装包的缓存, 以加快 npm install / pnpm install 的过程

3. 工作原理: 

    3.1 /cache/ 目录下的内容会构建成功后自动上传到服务器进行存储, 并在下次构建任务执行前进行挂载

    3.2 /cache/ 与 当前工作目录(即 './', 拉取的源码存放位置) 不在同一个文件系统(相当于是缓存在C盘而源码在D盘), pnpm install的行为将从 hark link回退为文件复制(硬链接的方式相对于大量小文件的拷贝, 速度要快很多)

    3.3 /cache/ 的工作涉及上传、下载过程, 如果过大也将会影响整个构建过程的速度

ビルド速度に対するグローバル キャッシュの影響を除外する

/cache/ のサイズを確認します。コンパイル コマンドに追加できます。du -sh /cacheログを確認します。フォルダーが 1G を超える場合 (参考のみ)、J-one に問い合わせてアプリケーション キャッシュをクリーンアップすることをお勧めします。

クロスディスクキャッシュによって引き起こされるパフォーマンス損失を解決する

主なアイデア: ソース コードと /cache/ を同じファイル システムに作成する 現在、この方法は pnpm アプリケーションに推奨されています。

原則: ソースコードと /cache/ を同じファイルシステムに保持することで、pnpm のハードリンク方式が有効になり、node_modules に数万個の小さなファイルをコピーするのに比べて、実行効率が大幅に向上します。複数のドライブまたはファイル システムにまたがる Pnpm 作業は可能ですか?

方法: 現在の作業ディレクトリのコードを /cache/ にコピーし、インストールおよびビルド コマンドを実行します。

参考コマンド

    # 记下当前工作目录
    CUR_WORKSPACE=`pwd`
    # 存放源码
    # 咱统一用 /cache/source 放源码就好, 虽然也可以改成其它目录的名字
    mkdir -p /cache/source
    # 拷贝当前目录的代码, 到 /cache/source 下
    rsync -r ./ /cache/source --exclude=node_modules --exclude=.git
    # 切换 workspace
    cd  /cache/source
    ########## 这里替换成自己需要的内容  ###########
    # 执行 install
    pnpm i
    # 执行 build
    pnpm run build
    ########## 这里替换成自己需要的内容  ###########

    # 将构建结果拷贝到抽包地址
    ########## 如果不是 dist, 请根据需要换成其它目录, 就是你项目构建完生成的目标代码目录
    cp -r ./dist/* ${CUR_WORKSPACE}/.build
    # 删除不需要被缓存的文件
    cd ../ && rm -rf /cache/source

上記のコンパイルコマンドは、Xingyun デプロイメントフロントエンドプロジェクト自体に基づいて簡略化されていますので、
原則と考え方を理解した上で、必要に応じて修正してください。

ビルド結果をキャッシュする

webpack とそのプラグインはビルド結果をキャッシュします。/cache/ の永続キャッシュを使用してコード ビルド キャッシュを実装できます。他のビルド ツールも関連ドキュメントを参照して設定できます。

webpack4 または webpack4 に依存するビルド ツール (@vue/cli-service など) を使用する場合は、通常、cache-loader を使用してビルド結果をキャッシュします。babel-loader にも独自のビルド キャッシュがありますが、デフォルトでは、node_modules/ に配置されます。キャッシュ ディレクトリの下では、関連ドキュメントを参照して、キャッシュ ディレクトリを /cache/build (または /cache/ の他のサブディレクトリ) に設定することをお勧めします。

webpack5ではキャッシュ機能が統合されており、キャッシュローダーなどのプラグインを削除することで無駄な作業を軽減できます。

モノリポジトリ アプリケーションの場合は、 nxを使用してモノリポジトリを管理するなど、サブプロジェクト レベルのキャッシュも実装できます。キャッシュ アドレスを設定するように NX_CACHE_DIRECTORY を設定できます。例:

export NX_CACHE_DIRECTORY=/cache/jdos3-console-ui/.nx

eslint は非常に時間のかかる操作でもあります。キャッシュもサポートしていますが、デフォルトでは有効になっていません。必要に応じてキャッシュを有効にすることもできますが、各ビルド ファイルの createTime は変更すると、メタデータ戦略は失敗します。参考: eslint キャッシュ

通常、ローカル開発とクラウド展開の両方に互換性がある必要があり、これは環境変数によって実現できます。

webpack5 のキャッシュ構成:

{
    cache: {
        type: 'filesystem',
        profile: true,
        cacheDirectory: process.env.BUILD_CACHE_DIRECTORY,
        compression: 'gzip',
    },
}

同時に、Xingyun デプロイメントのコンパイル コマンドに追加します。

export BUILD_CACHE_DIRECTORY=/cache/.webpack

キャッシュを使用する別の方法: キャッシュnode_modules

(コンパイル チームがこのアイデアを提案しましたが、私はまだ試していません。製品でのこのアイデアの一般的な解決策は検討中です) 主なアイデア : ローカル ビルドをシミュレートする (ローカル ビルドでは、node_modules
ディレクトリが永続的に保持されます)
利点:
1. 高速化インストール 2. コードを使用して
キャッシュを構築します: webpack5 または babel-loader は通常、ビルド キャッシュを node_modules/.cache ディレクトリに保存します。これが、多くのアプリケーションのビルドがローカルで高速になる理由です。もちろん、.cache ディレクトリは成長し続けます。興味がある場合は、このディレクトリがローカル コードに存在するかどうか、およびこのディレクトリがどれくらいのスペースを占有しているかを確認できます。

参考コマンド
上記の「クロスディスクキャッシュによるパフォーマンス低下の解決」のプロセスとほぼ同じですが、最後のrmプロセスで次回の使用のためにnode_modulesディレクトリを保持する点が異なります。

    ####### 与上面 解决缓存跨盘造成的性能损失 一致 #########
    # 记下当前工作目录
    CUR_WORKSPACE=`pwd`
    # 存放源码
    mkdir -p /cache/source
    # 拷贝当前目录代码到 /cache/ 下
    rsync -r ./ /cache/source --exclude=node_modules --exclude=.git
    # 切换 workspace
    cd  /cache/source
    # 执行 install
    npm i
    # 执行 build
    npm run build
    # 将构建结果拷贝到抽包地址
    cp -r ./dist/* ${CUR_WORKSPACE}/.build
    
    ####### 差异: 删除时排除 node_modules 目录 #########
    # 删除不需要被缓存的文件
    ls -A | grep -vE "^\.$|^\.\.$|^node_modules"|xargs rm -rf

ソースコードを減らす

コーディング中に、node_modules やさまざまな大きなバイナリ ファイルを送信しないようにします。

コンパイルプロセスを最適化する

依存パッケージのインストールプロセスを最適化する

  1. 一部のプロジェクトでは、画像圧縮ツールである image-minimizer-webpack-plugin に依存していますが、このリソースが依存している cwebp-bin やその他のリソースは海外の Web サイトからダウンロードする必要があり、このプロセスが遅くなるか失敗する可能性があります。可能であれば、圧縮イメージをコード ベースに直接送信し、このプラグインへの参照を削除することをお勧めします。
  2. たとえば、コンパイル コマンドの前に時間を追加して、time pnpm installこのステップの時間がかかることを確認できます。このステップが非常に長い場合は、削除できる依存パッケージがあるかどうかを確認したり、オプションの依存パッケージのインストールを無効にしたりできます。 、場合によってはビルドをアップグレードする ツールを使用すると、パッケージの依存関係を最適化することもできます。

ビルドプロセスを最適化する

  1. Webpack によって構築されたアプリケーションの場合は、不必要なファイル構築を減らすために、ルールとプラグイン (サポートされている場合) に対して除外が正しく設定されているかどうかを確認してください。
  2. ビルド キャッシュを有効にします (ただし、キャッシュの継続的な増加には依然として注意が必要で、過剰なキャッシュの問題は後で製品レベルから最適化される可能性があります)
  3. ts-loader は通常、transpileOnly: true を有効にし、 fork-ts-checker-webpack-pluginを通じて型チェックを実行できます。
  4. eslint の最適化により、ルールを最適化できます。一部の検証ルールは非常に時間がかかりますが、同時に利点はそれほど大きくありません。それらをオフにすることを検討できます。具体的には、次のことができます。

4.1 __TIMING__ 環境変数を設定すると、各 eslint ルールのパフォーマンス分析が可能になります; export TIMING = 14.2
ローカルで通常通りビルドを実行し、eslint ルールのパフォーマンス出力を検出し、時間のかかるルールを分析し、必要かどうかを確認します

補足

  • eslint のマルチスレッドの問題について: eslint でマルチスレッドが有効になった後、ビルド プロセスで見つかったルール例外がスローされず、ルールが実際に失敗します。この問題については、「問題」を参照してください。この問題は以前から存在していまし。長い間、効果的に解決されていませんでした。
  • 同時に、非標準コードの送信を避けるために、eslint 検証を git フックとして実装することも検討できます。この時点では、ビルド プロセス中にこの手順を省略できます。

5. コードの minify の処理には、webpack で設定できる esbuild の使用を推奨します。

{
   optimization: {
       minimize: true,
       minimizer: [
           new TerserPlugin({
               minify: TerserPlugin.esbuildMinify,
           }),
       ],
   }
}

6. 頻繁に変更しない部分については、事前にコンパイルするか、 DllPluginを通じて最適化することをお勧めします。たとえば、Xingyun Deployment プロジェクト自体は monaco エディターに依存していますが、毎回ソースコードをビルドするのは時間がかかります。そのため、プリコンパイルされたコードを直接コンパイルして送信し、後で直接使用します。

7. プロジェクトが複数回ビルドされないよう注意してください。例:
7.1 v5.0.0-beta.0 以降、vue-cli-service を使用するアプリケーションの場合、ブラウザー リストの構成に従って異なるパッケージが生成される場合があります。複数のビルドにつながる
7.2 一部のプロジェクトでは、マイクロ フロントエンド アクセスが必要であり、独立したランタイム モードとサブアプリケーション モードに異なるエントリを使用する場合があるため、2 回ビルドされます。たとえば、 JModule のユーザーは、非常に初期のバージョンのwebpack -jmodule-plugin ではサポートされないため、エントリ ファイルをカスタマイズします。通常は 2 回ビルドします。最新の @jmodule/plugin-webpack にアップグレードし、同じエントリ ファイルを使用して 1 回ビルドすることをお勧めします。

8. 比較的単純なアプリケーションの場合は、esbuild、swc などの他のビルド ツールに切り替えることを検討できますが、プログラミング言語によってもたらされるパフォーマンスの違いが、実際に次元削減に打撃を与える可能性があります。

9. 可能であれば、プロジェクト コード間の依存関係を分析し、複数のビルドに分割して並列実行するコンパイラの最大の利点はマルチコアであり、それを最大限に活用できます。

10. webpack やその他のビルド プラグインをアップグレードすると、通常、ある程度の速度向上がもたらされ、jci プロジェクトのコンパイルはアップグレードの恩恵を受けました。

補足

  1. Webpack の最適化の詳細については、https://webpack.docschina.org/configuration/cache/を参照してください。
  2. time npm run build同様に、このステップの時間を観察しやすくするために、ビルド コマンドの前に時間を追加することも検討できます (例: ) 。
  3. 「speed-measure-webpack-plugin」を使用して、Webpack の構築時間を分析することもできます。

フロントエンド構築の高速化は比較的複雑かつ詳細なプロジェクトです。現在、製品はビルドの遅いアプリケーションを継続的に追跡し、コンパイル速度の最適化を試みています。ただし、フロントエンド自体は比較的自由な技術環境があり、統一された構築ツールやプロセスが存在せず、また、言語自体の実行効率やシングルスレッド構築がコンパイラの能力を最大限に発揮するには十分ではないため、現在の世界的な一般的な最適化手法は依然として比較的限定的であり、それは依然としてプロジェクト自体の最適化にかかっていますが、より良い明日を築くために全員が協力することを願っています。

著者: JD Technology 林光輝

コンテンツソース: JD Cloud 開発者コミュニティ

{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4090830/blog/9093023