web:10から1のコンパイルへの大きなリファクタリング

はじめに:大規模なフロントエンドプロジェクトでは、構築の安定性と使いやすさが非常に重要です。Tencentドキュメントの反復プロセスでは、複雑なプロジェクト構造とコンパイルの問題が日々増加しているため、新規参入者は毎日始めます。レンガを移動するコスト。Webpack5の発売と同時に、完全な魔法の変更を行うことをお勧めします〜

1はじめに

Tencent Documentationによってリリースされたwebpack5は、コンパイルの大幅なリファクタリングを受けています。複数のウェアハウスで構成される大規模なプロジェクトとして、どのカテゴリのコードの量も100万を超えています。Tencent Documentsのように、高速に反復し、自動パイプラインに大きく依存し、年間を通じて複数の大規模プロジェクトと多数の小規模プロジェクトを並行して実行するプロジェクトの場合、開発効率には安定した高速コンパイルが不可欠です。この記事は、著者が最近のリファクタリング中に遭遇し、毎日の開発を1に最適化することに成功した、いくつかの大規模プロジェクトに固有の問題と考えについてです。フロントエンドプロジェクトの最適化にいくつかの参考資料とアイデアがもたらされることを願っています。建設。インスピレーションを得た。

2.大規模なプロジェクトをコンパイルすることの苦痛

プロジェクトシステムが徐々に拡張されると、古いコンパイル構成で新しい機能をサポートできないことがよくあります。さまざまな構成ファイルに付属する読み取りのデバフと、技術的負債が大きいため、誰もが変更しない傾向があります。古い構成であり、周辺のコンパイルシステムを修正するためにいくつかの構成を追加する試みです。同様の理由で、過去のTencentドキュメントの編集と編集はエレガントではありませんでした。

img

マルチレベルのサブリポジトリ構造と複雑なコンパイルシステムは、高い理解と変更のコストを引き起こし、また長いコンパイル時間をもたらし、チーム全体の開発効率に大きな影響を与えます。

3.オールインワン

複雑で遅いコンパイルの問題を解決するには、人形の入れ子を禁止することが重要です。マルチレベルの混合システムを廃止する必要があり、統合されたコンパイルが重要です。すべてのコンパイルシステムの中で、Webpackは大規模なプロジェクトのパッケージ化に大きな利点があり、プラグインシステムが最も豊富で、Webpack 5はモジュールフェデレーションの新機能をもたらすため、Webpackを使用して複数のサブのコンパイルを統合することにしました。 -倉庫。

3.1。lernaベースのリポジトリ構造の統合

Tencentのドキュメントでは、倉庫内のサブパッケージを管理するためにlernaを使用していますが、lernaを使用する利点はここでは拡張されません。ただし、lernaの一般的な使用法にも問題があります。Lernaは、ウェアハウスを構造内の複数のウェアハウスに変換します。デフォルトの使用方法を使用すると、各ウェアハウスに独自のコンパイル構成があり、単一のプロジェクトのコンパイルは次のようになります。構成を変更し、複数のプロジェクトのコンパイルとデバッグを段階的に最適化することは困難です。

lernaを使用する目的は、各サブパッケージを比較的独立させることですが、プロジェクト全体のコンパイルとデバッグでは、すべてのパッケージのコレクションが必要になることがよくあります。その後、作成者はサブパッケージ間の物理的な分離を無視できます。サブウェアハウスをサブパッケージとして使用します。ディレクトリを確認します。lernaに依存せずに、解決する必要があるのは、サブパッケージ間の参照の問題です。

/** package/layout/src/xxx.ts **/
import {
    
     Stream } from '@core/model';
// 做求和

実際、作成者はWebpack構成のresolveのエイリアス属性を介して対応する効果を達成できます。

{
    
    
  resolve: {
    
    
    alias: {
    
    
      '@core/model': 'word/package/model/src/',
    }
  }
}

3.2。パッケージングシステム外でのファイルの管理

大規模なプロジェクトでは、特別な静的コードファイルが存在する場合があります。これらのファイルは、パッケージシステムに参加しないことがよくありますが、他の方法でhtmlに直接インポートされるか、最終結果にマージされます。

このようなファイルは通常、次のカテゴリに分類されます。

img

  1. 読み込み時間が早い外部SDKファイル、それ自体がミニファイファイルとして提供されます
  2. jqueryなど、外部ファイルが依存する他のフレームワークファイル
  3. いくつかのポリフィル
  4. SDKの初期化など、早期に実行する必要のある特別な独立したロジック。

ポリフィルと外部SDKは、グローバル変数をぶら下げるモードを直接実行することが多いため、プロジェクトは、htmlスクリプトタグを直接書き込むことによってそれらを参照することがよくあります。ただし、このようなファイルの増加に伴い、ラベル参照の直接使用はバージョン管理およびコンパイルプロセスに適しておらず、対応する初期化ロジックの一部をパッケージ化プロセスに追加できません。この場合、作成者はjsエントリファイルを手動で作成し、上記のファイルを参照して、webpackのエントリとして使用することをお勧めします。このように、これらのバルクファイルはコードで管理できます。

img

import 'jquery';
 
import 'raven.min.js';
import 'log.js';
// ...

ただし、一部の外部jsはjQueryなどの他のsdkに依存している場合がありますが、パッケージングシステムはそれらの間の依存関係を認識していないため、jQueryは時間内にグローバルに公開されません。どうすればよいですか?実際、webpackは、これらの問題に対処するための非常に柔軟なソリューションを提供します。たとえば、作成者は、サードパーティの参照用にexpose-loaderを介してjQueryをグローバルに公開できます。Tencentのドキュメントには、リモートcdn用のいくつかのSDKコンポーネントも含まれており、これらのSDKはjQueryなどのいくつかのライブラリも参照する必要があります。したがって、作成者は、splitChunksの構成によってjQueryを分離し、cdnに基づいてロードされたsdkも正常に初期化できるように、より早いロード時間に配置しました。

img

コード参照により、依存ファイルのバージョン管理を適切に実行できます。また、対応するファイルのコンパイルもパッケージ化プロセスに追加されるため、対応するファイルへのすべての変更を動的に監視できます。 、これは後続の追加に役立ちます。ボリュームのコンパイル。同時に、webpackのカプセル化特性により、各ライブラリはwebpack_requireの特別な関数に含まれ、公開されるグローバル変数の数をより制御しやすくなります。

3.3。カスタマイズされたwebpackプロセス

Webpackは、html生成用の非常に柔軟なhtml-webpack-pluginを提供します。テンプレートと多数の専用プラグインをサポートします。ただし、プロジェクトにはまだいくつかの特別な要件があり、一般的なプラグイン構成はこれらの要件を満たすことができないか、マッチング結果を理解するのは非常に困難です。これは、Tencent Documentsが最初にgulpを使用してhtmlを生成した理由でもあります。gulp構成には、TencentDocumentsの公開要件を満たすための多くのカスタムプロセスがあります。

gulpはプロセスをカスタマイズしてhtmlを生成できるため、カスタマイズされたプロセスを実装するための別のWebpackプラグインを作成することもできます。

Webpack自体は非常に柔軟なシステムです。特定のプロセスに従って実行されるフレームワークです。各プロセスのさまざまな段階でさまざまなフックを提供します。さまざまなプラグインを使用してこれらのフックのコールバックを実装し、コード。実際、webpack自体は無数のネイティブプラグインで構成されています。このプロセスを通して、私はそれをカスタマイズするためにあらゆる種類の異なることをすることができます。

img

プラグインを追加してhtmlを生成するシナリオでは、生成されたファイルをwebpack処理する段階で、生成されたjs、css、その他のリソースファイル、およびejsテンプレートと特別な構成が統合されてから追加されます。 webpackのアセットコレクションに、カスタムhtml生成を完了することができます。

img

compilation.hooks.processAssets.tap({
    
    
  name: 'TemplateHtmlEmitPlugin',
  stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
}, () => {
    
    
  // custom generation
  compilation.emitAsset(
    `${
      
      parsed.name}.html`,
    new sources.RawSource(source, true),
  );
  compilation.fileDependencies.addAll(dependencies);
});

上記のコードでは、最後の文に気付くことができます。compilation.fileDependencies.addAll(dependencies)この文を通じて、作成者はカスタム生成が依存するすべてのファイルをwebpack依存関係システムに追加でき、これらのファイルが変更されると、webpackは対応する生成プロセスを自動的にトリガーできます。 。

3.4。ワンクリック開発エクスペリエンス

これまでのところ、すべてのコンパイルが統合されており、プロジェクト全体を1つのWebpackでコンパイルできます。また、watchとdevServerを一緒にハイにすることもできます。ただし、コンパイルは統合できるので、すべての操作を統合してみませんか?

node_modulesを手動で操作してはならないという前提に基づいて、作成者はpackage.jsonに依存関係のスナップショットを作成し、パッケージの変更に応じて毎回再インストールする必要があるかどうかを判断できます。コードを同期し、不要な手順をスキップします。

public async install(force = false) {
    
    
  const startTime = performance.now();
  const lastSnapshot = this.readSnapshot();
  const snapshot = this.createSnapshot();
 
  const runs = this.repoInfos.map((repo) => {
    
    
    if (
      this.isRepoInstallMissing(repo.root)
      || (!repo.installed
      && (force || !isEqual(snapshot[repo.root], lastSnapshot[repo.root])))
    ) {
    
    
      // create and return install cmd
    }
    return undefined;
  }).filter(script => !!script);
 
  const {
    
     info } = console;
  if (runs.length > 0) {
    
    
    try {
    
    
      // 执行安装并保存快照
      await Promise.all(runs.map(run => this.exec(run!.cmd, run!.cwd, run!.name)));
      this.saveSnapshot(snapshot);
    } catch (e) {
    
    
      this.removeSnapshot();
      throw e;
    }
  } else {
    
    
    info(chalk.green('Skip install'));
  }
  info(chalk.bgGreen.black(`Install cost: ${
      
      TimeUtil.formatTime(performance.now() - startTime)}`));
}

同様に、Tencentドキュメントのローカルデバッグは、プロキシ用のwhislteを介して特別なテスト環境に基づいており、そのような手順も自動化できます。その後、開発では、すべてが非常に簡単で、1つのコマンドで、レンガを簡単に移動できます〜

img

ただし、複雑なシステムであるため、必ず初めて初期化する必要があります。コンパイルシステムの依存関係がインストールされておらず、鶏がいない場合、どうすれば産卵できますか?

実際、そうでなければ、コンパイルシステム全体の外層に赤ちゃんを作り、フロントエンド開発を行う方がよいでしょう。ノードは常に最初にインストールされますよね?次に、現在のコンパイルコマンドを実行する前に、作成者はノードのみに依存するスクリプトを実行します。このスクリプトはメインコマンドを実行しようとします。メインコマンドが直接クラッシュする場合は、インストール環境の準備ができていないことを意味します。時間、コンパイルシステムが初期化されますそれは大丈夫です。このようにして、ワンクリック開発を実際に実現できます。

const cmd = '启动编译的命令';
const main = extraArg => childProcess.execSync(`${
      
      cmd} ${
      
      extraArg}`, {
    
     stdio: 'inherit', cwd: __dirname });
 
try {
    
    
  main('');
} catch (e) {
    
    
  // 初始化
  main('after-initialize');
}

3.5.システムコーディングをコンパイルする

このリファクタリングプロセスでは、作成者は元のコンパイル構成をtsに変更してwebpackのnodeApiを呼び出してコンパイルを実行しました。体系化されたビルドシステムには多くの利点があります。

  1. API呼び出しを使用すると、IDEによってもたらされるコードヒントを楽しむことができ、構成ファイルに誤ってタイプミスを入力したため、一日中デバッグする必要がなくなります。
  2. コードAPIを使用すると、コンパイルされた構造をより適切に実装できます。特に、複数の出力がある場合は、構成ファイルの単純な組み合わせよりも管理する方が適切です。
  3. コード化されたコンパイルシステムを使用すると特殊効果があり、コンパイルシステムはテストを作成することもできます。何?テストを書くためにシステムをコンパイルしますか?実際、Tencentのドキュメントの以前のリリースでは、いくつかの不可解なバグがありました。リリース前のテストでは、プログラム全体のパフォーマンスが突然異常になりました。関連するコードに変更はありません。カーペットを長期間調査した結果、コンパイル結果が以前の結果とわずかに異なることがわかりました。実際、システムテスト環境が生成されてから最初の5時間で、コンパイルに依存するプラグインが小さなバージョンでサイレントに更新され、作成者はプラグインパッケージにデフォルトの^xx.xxを使用しました。 json、およびパイプラインが最新バージョンにインストールされ、クラッシュが発生しました。その時、作者は、関連するライブラリをコンパイルするにはバージョンをロックする必要があるという結論に達しました。ただし、バージョンをロックしても実際には問題は解決しません。コンパイルで使用されるコンポーネントは常に1日でアップグレードされます。このアップグレードで問題が発生しないことが保証されている場合はどうなりますか?これは自動テストの領域です。実際、Webpackのコードを見ると、一貫性をコンパイルするために多くのテストケースを実行していることがわかります。ただし、Webpackにはさまざまなプラグインがあり、すべての作成者が品質保証に十分な投資をしているわけではありません。 、自動テストを使用してコンパイルシステムの安定性を確認することも、詳細に調査できるトピックです。

4.コンパイルのスピードアップ

タイプスクリプトのコンパイルを伴うプロジェクトでは、基本的な高速化操作は非同期型チェックであり、ts-loaderとfork-ts-checkerのtranpsileOnlyパラメーターの組み合わせは決して面倒ではありません。ただし、複雑な大規模プロジェクトの場合、このコンビネーションパンチのセットのアクティブ化プロセスはスムーズに進まない可能性があります。著者をフォローして、Tencentドキュメントでの高速コンパイルを可能にするでこぼこの道を確認することをお勧めします。

4.1。消える列挙型

transpileOnlyパラメーターを有効にした直後、コンパイル速度は大幅に向上しましたが、結果は楽観的ではありませんでした。コンパイル後、ページを開く前にページがクラッシュしました。エラーレポートによると、依存関係ライブラリからインポートされたオブジェクトが未定義になり、プログラムがクラッシュしたことがわかりました。未定義になるこのオブジェクトは列挙型であり、次のように定義されます。

export const enumScope{
    
    
  VAL1= 0,
  VAL2= 1,
}

transpileOnlyを有効にすると、なぜ空になるのですか?これはその特別なプロパティと関係があり、通常の列挙型ではなく、定数列挙型です。ご存知のとおり、列挙型はtsの糖衣構文です。各列挙型は、js内のオブジェクトに対応します。したがって、通常の列挙型をjsに変換すると、次のようになります。

// ts
export enum Scope {
    
    
  VAL1 = 0,
  VAL2= 1,
}
 
const a = Scope.VAL1;
 
// js
constScope= {
    
    
  VAL1: 0,
  VAL2: 1,
  0: 'VAL1',
  1: 'VAL2',
};
 
const a =Scope.EDITOR;

constキーワードをスコープに追加するとどうなりますか?次のようになります。

// ts
export const enumScope{
    
    
  VAL1= 0,
  VAL2= 1,
}
 
const a = Scope.VAL1;
 
// js
const a = 0

つまり、const enumはマクロと同等であり、jsへの変換後には存在しません。しかし、transpileOnlyがオフになっていると、コンパイル結果が正常に機能するのはなぜですか?実際、外部ライブラリの宣言ファイル.d.tsを注意深く見ると、この.d.tsファイルではスコープがそのまま保持されていることがわかります。

// .d.ts
export const enumScope{
    
    
 VAL1= 0,
 VAL2= 1,
}

通常のコンパイルプロセスでは、tscは.d.tsファイルをチェックし、すでにこの定義を予測しているため、マクロ変換を正しく実行できます。transpileOnlyがオンになっている場合、元のタイプが無視されるため、すべてのタイプが無視されます。スコープはのライブラリモジュールに存在しなくなったため、コンパイル結果を正常に実行できません(PS:tscは、トランスパイルモードでのコンパイルは解決しないと公式に述べています。d.tsは標準機能であり、constが失われます。列挙型はバグではないので、公式のサポートを待つことは無益です)。原因がわかったので、修正できます。4つのオプション:

  • オプション1は、公式ガイダンスに従います。constenumをエクスポートせず、内部で使用される列挙型のconstのみをエクスポートします。つまり、依存ライブラリを変更する必要があります。もちろん、今回のTencentのドキュメントクラッシュのすべての依存ライブラリは独自のSDKに属していますが、外部ライブラリが問題を引き起こした場合はどうなりますか?したがって、計画は安全ではありません。
  • 完璧なバージョンであるオプション2は、.d.tsファイルを手動で解析し、すべての定数列挙型を検索して定義を抽出します。ただし、transpileOnlyによって取得されるコンパイルアクセラレーションは、.d.tsファイルを無視することで実際にメリットがあります。作成者が手動で.d.tsを列挙型として解析する場合、.d.tsファイルには複雑な参照リンクが含まれる可能性があります。の消費。

img

  • オプション3、文字列置換、const enumはマクロであるため、string-replace-loaderを使用して手動で同様の効果を実現できます。ただし、文字列の置換方法は依然として暴力的です。Scope['VAL1']と同様の使用法を使用すると、予期せず失敗する可能性があります。

img

  • オプション4は、作成者が最終的に採用したソリューションでもあります。定義が消えたため、再定義することをお勧めします。WebpackのDefinePluginを使用して、作成者は欠落しているオブジェクトを再定義し、コンパイルの通常の解析を保証できます。
new DefinePlugin({
    
    
  Scope: {
    
    VAL1: 0,VAL2: 1 },
})

4.2.愛憎デコレータと依存性注入

残念ながら、コンパイルされたオブジェクトが見つからないという問題を解決するためだけに、コードを実行することはできません。プログラムを初期化しても失敗します。デバッグを行った後、初期化プロセスに微妙な違いがあることがわかりました。明らかに、transpileOnlyをオンにすると、コンパイル結果が変更されます。
この問題を解決するには、transpileOnlyパターンの実装を詳しく調べる必要があります。transpileOnlyの最下層は、tscのtranspileModule関数に基づいて実装されます。transpileModuleの役割は、各ファイルを独立した個人として解析することであり、各インポートはモジュール全体として扱われます。コンパイラは、モジュールのエクスポートとファイルを解析しません。 。特定の関係。例:

// src/base/a.ts
export class A {
    
    }
 
// src/base/b.ts
export class B {
    
    }
 
// src/base/index.ts
export * from './a';
export * from './b'
 
// src/app.ts
import {
    
     A } from './base';
 
const a = new A();

上記は一般的なコード記述方法です。多くの場合、index.tsを介してベースのモジュールをエクスポートするため、他のモジュールでは、作成者がファイルを参照する必要はありません。通常モードでは、エディターはこのコードを解析し、情報を添付してAがa.tsによってエクスポートされることをwebpackに通知します。したがって、webpackがパッケージ化されている場合、特定のシナリオに従ってAとBを異なるファイルにパッケージ化できます。ただし、transpileModuleモードでは、webpackはベースモジュールがAをエクスポートすることのみを認識しますが、どのファイルAがエクスポートされるかは認識しません。したがって、現時点では、webpackはモジュール全体としてAとBを1つのファイルに確実にパックします。 、アプリで利用できます。Tencentドキュメントの場合、この状況は次のように変更されました(モジュールは、1、2、3、4、および5の順序でロードされ、モジュールの視覚的なサイズはサイズを示します)。

img

transpileOnlyをオンにすると、多数のファイルがモジュール1にパッケージ化され、事前にロードされていることがわかります。ただし、一般的に、モジュールがパッケージ化されている場所は、コードの動作に影響を与えるべきではありませんか?結局のところ、コード分割がオフになっている場合、コードを解凍する必要はありません。一般的なケースでは、この理解は間違っていません。ただし、デコレータを使用するプロジェクトの場合は機能しません。コードが一般的にes5に変換される時代では、デコレータは__decorator関数に変換されます。これは、コードがロードされるときに自己実行する関数です。コードのパッケージ順序が変わると、自己実行関数の実行順序も変わる可能性があります。では、これによりTencentドキュメントが正常に起動しなくなるのはなぜですか?これは、Tencentドキュメントでの依存性注入テクノロジーの包括的な導入から始まります。

Tencentドキュメントでは、各関数は機能です。この機能は手動で初期化されるのではなく、特別なデコレータを介してTencentドキュメントのDIフレームワークに注入され、統合インスタンス作成のために注入フレームワークによって作成されます。たとえば、通常の変更では、3つの機能A、B、C、A、Bがモジュール1にコンパイルされ、Cがモジュール2にコンパイルされます。モジュール1がロードされると、ワークベンチはインスタンスの作成と初期化のラウンドを実行します。このとき、FeatureAの初期化は特定の副作用をもたらします。次に、モジュール2がロードされ、ワー​​クベンチが強度の作成と初期化の別のラウンドを実行します。この時点で、FeatureCの初期化はFeatureAの副作用に依存していますが、初期化の最初のラウンドが終了しているため、Cは正常にインスタンス化されます。

img

transpileOnlyをオンにすると、すべてが変更されます。エクスポートを区別する方法がないため、機能A、B、およびCは同じモジュールにパッケージ化されます。機能Cを初期化すると、副作用が発生していないため、Cの初期化に失敗することが考えられます。

img

transpileOnlyは本質的に依存性注入と互換性がないため、修正する方法を見つける必要があります。の場合、作成者はアプリ内の参照を置き換えます。

// src/app.ts
import {
    
     A } from './base/a';
 
const a = new A();

モジュールエクスポートの分析問題は解決しましたか?ただし、非常に多くのコードをそのような参照に変更することは、醜いだけでなく、反人間的であり、作業負荷も膨大です。そこで、コンパイル時に問題を解決するためのプラグインとローダーの組み合わせを設計しましょう。コンパイルの初期段階で、作成者はプラグインを介してプロジェクトファイルを解析し、エクスポートを抽出し、各エクスポートとファイルの間の対応する関係を見つけて保存します(ここでは、IOの読み取りと書き込みのパフォーマンスが心配になる場合があります現在の開発者はすべて高速SSDであることを考慮すると、このIOスループットは実際には何もありません。このエクスポート解像度の実際の測定値は1秒未満です)。次に、コンパイルプロセス中に、作成者はカスタムローダーを対応するインポートステートメントに渡します。 、通常の書き込みコードに影響を与えることなく、transpileOnly解析の有効性を維持できるようにします。

img

何度も投げた後、最終的に、Tencentドキュメントは高速コンパイルモードで正常に実行され、所定のコンパイル速度に達しました。

5.Webpack5アップグレードロード

5.1。いくつかの互換性の問題の処理

結局のところ、Webpack5は互換性のないアップグレードであり、Tencentのドキュメントコンパイルシステムの再構築中に、多くの問題も発生しました。

5.1.1.SplitChunksカスタムChunkGroupsエラー

splitChunksのヘビーユーザーでもある場合、webpack5をアップグレードするプロセス中に、次の警告が表示される場合があります。

img

この警告の説明はあまり明確ではありません。言い換えると、このプロンプトが表示され、chunkGroups構成で、モジュールがグループAとBの両方に属していることを示します(ここで、AとBは2つのエントリポイントまたは2つの非同期モジュールです)。モジュールがAに​​属する場合を明示的に指定します。なぜWebpack5はこの時点で警告を報告するのですか?一般に、モジュールは2つのエントリポイントまたは非同期モジュールに属しているため、モジュールはパブリックモジュールとして抽出する必要があります。モジュールがAに​​属している場合、モジュールBを個別にロードすると、モジュールBを正常にロードできません。

ただし、一般に、そのような指定が発生した場合、それが構成エラーでない場合は、AとBの間に明確なロード順序がすでに存在します。しかし、この読み込み順序、Webpackは知りません。エントリポイントの場合、webpack5では、dependOn属性を使用してエントリ間の依存関係を指定できます。ただし、非同期モジュールの場合、そのようなトラバーサル設定はありません。もちろん、作成者はカスタムプラグインを使用して、最適化する前にwebpackが既存のモジュールの依存関係と変更に関する追加情報を確実に認識できるようにすることもできます。

compiler.hooks.thisCompilation.tap('DependOnPlugin', (compilation) => {
    
    
  compilation.hooks.optimize.tap('DependOnPlugin', () => {
    
    
    forEach(this.dependencies, (parentNames, childName) => {
    
    
      const child = compilation.namedChunkGroups.get(childName);
      if (child) {
    
    
        parentNames.forEach((parentName) => {
    
    
        const parent = compilation.namedChunkGroups.get(parentName);
        if (parent && !child.hasParent(parent)) {
    
    
          parent.addChild(child);
          child.addParent(parent);
        }
        });
      }
    });
  });
});

5.1.2。プラグインが依存するAPIが削除されました

Webpack5のリリース後、すべての主要な主流プラグインが次々に適応され、プラグインを最新バージョンに更新するだけで済みます。ただし、多くの理由で時間内に更新されないプラグインもあります。(追記:現在、一致しないプラグインのほとんどはすでに比較的小さいです。)要するに、この問題は比較的解決されていませんが、適切に待つことができます。近い将来、ほとんどのプラグインは実際、webpack5に適応します。webpack5も使用されます。多くの名前が変更され、一部のインターフェースが転送され、呼び出し方法が変更されましたが、すべてが劇的に変更されたわけではありません。小さなプラグインの場合は、自分でフォークしてみてください。

5.2。モジュールフェデレーションの最初の経験

通常、大規模なプロジェクトの場合、作成者はプロジェクト間のモジュール共有を改善するために多くの共通コンポーネントを抽出しますが、これらのモジュールには、React、ReactDOM、JQueryなどの基本ライブラリなどの共通の依存関係が必然的にあります。このように、問題が発生しやすく、共通のコンポーネントが抽出された後、プロジェクトのボリュームが拡大します。パブリックコンポーネントの増加に伴い、プロジェクトボリュームの拡大は非常に恐ろしいものになります。従来のパッケージモデルでは、作成者はシンプルで効果的な方法を見つけました。パブリックコンポーネントの場合、作成者は外部を使用してこれらのパブリックパーツを切り取り、無効なコンポーネントに変換します。

img

ただし、コンポーネントの数が増えると、共有コンポーネントのホストの数が増えるため、いくつかの問題が発生します。

  1. コンポーネントは、ホスト用に特別にパッケージ化する必要があります。独立して実行できるコンポーネントではありません。コンポーネントを実行する各ホストは、完全なランタイムを実行する必要があります。そうでない場合、コンポーネントは、ホストごとに異なる障害パッケージを印刷する必要があります。
  2. コンポーネントとコンポーネントの間に大きな共有モジュールがある場合、外部から解決することはできません。

このとき、静的パッケージから完全なランタイムへのWebpackの変換であるモジュールフェデレーションが登場しました。モジュールフェデレーションでは、ホストとリモートの概念が提案されました。リモートのコンテンツはホストによって消費できます。この消費プロセスでは、webpackの動的ロードランタイムを介して必要な部分のみをロードでき、既存の部分は2回ロードされません。(下の図では、jQuery、react、duiがすでにホストに含まれているため、WebpackランタイムはRemote1にComponent1、Remote2にComponent2のみをロードします。)

img

つまり、リモートとして、パブリックコンポーネントには完全なランタイムが含まれ、ホストはリモートを実行するために準備する必要のあるランタイムを知る必要はありませんが、Webpackのローダーは共有コードがロードされないようにします。このようにして、従来の外部パッケージングモードでの多くの問題が回避されます。実際、コンポーネントはホストとリモートの両方にすることができます。つまり、プログラムはメインプロセスとオンラインSDKリポジトリの両方として実行できます。モジュールフェデレーションの実装原理については、ここでは繰り返しません。興味がある場合は、Tencent Documentsのアプリケーションで、webpack5の新機能であるモジュールフェデレーションの分析を参照できます。また、 module-federation-examplesリポジトリの例。

Webpack5のモジュールフェデレーションは動的ロードメカニズムに依存しているため、デモインスタンスでは次の構造を確認できます。

// bootstrap.js
import App from "./App";
import React from "react";
import ReactDOM from "react-dom";
 
ReactDOM.render(<App />, document.getElementById("root"));
 
// index.js
import('./bootstrap.js');

Webpackのエントリ構成はindex.jsで設定されます。これは、すべての依存関係を動的に決定してロードする必要があるためです。エントリが非同期チャンクに変換されない場合、依存関係を順番にロードできるようにするにはどうすればよいですか?結局のところ、Moudleフェデレーションの実装の中核はwebpack_requireに基づく動的ローディングシステムです。モジュールフェデレーションでは複数のウェアハウスをリンクする必要があるため、その進歩は比較的長いプロセスである必要があります。では、作成者は既存のプロジェクトをインデックスブートストラップ構造に直接変換する必要がありますか?実際、Webpackのプラグインメカニズムを使用して、この非同期プロセスを動的に実装できます。

private bootstrapEntry(entrypoint: string) {
    
    
const parsed = path.parse(entrypoint);
const bootstrapPath = path.join(parsed.dir, `${
      
      parsed.name}_bootstrap${
      
      parsed.ext}`);
this.virtualModules[bootstrapPath] = `import('./${
      
      parsed.name}${
      
      parsed.ext}')`;
return bootstrapPath;
}

上記のbootstrapEntryメソッドでは、作成者は元のエントリポイントファイルに基づいて仮想ファイルを作成します。このファイルの内容は次のとおりです。

	import('./entrypoint.ts');

次に、webpack-virtual-modulesプラグインを使用して、同じディレクトリに仮想ファイルが生成され、元のエントリが置き換えられ、module-federationの構造変換が完了します。このように、他の対応する構成を使用して、作成者は単純なパラメーターを使用してモジュールフェデレーションをオンまたはオフにし、プロジェクトをWebpack5対応の構造に変えることができます。一緒にオンライン。

6.追記

コンパイルのリファクタリングは作者が長い間計画していたもので、チームで働いていたときに、こんなに複雑なコンパイルリンクのあるプロジェクトに初めて触れたのを覚えています。私のプロジェクトの経験は浅すぎて、連絡先の編集とパッケージ化はとても簡単でした。多くの技術的な困難に加えて、大規模なプロジェクトで見つけなければならない先祖の構成も、プロジェクト。

Webpack 5のベータサイクルは非常に長いため、Webpack 5のリリース後、互換性の問題は予想ほど多くありません。ただし、Webpack 5のドキュメントは少し穴が開いています。NodeApi用でない場合は、タイプがあります。宣言、および作成者は多くの公式文書を見つけることは決してありません。与えられたパラメーターはまだwebpack4の古いデータであり、正しくありません。そのため、ソースコードをデバッグして正しい構成方法を見つけることに頭を悩ませなければなりませんでしたが、それから多くのことを得ることができました。さらに、Webpack5はまだ反復中であり、モジュールフェデレーションで不適切な構成を使用するなどのバグがまだあり、奇妙なコンパイル結果につながる可能性があり、エラーは報告されません。したがって、問題が発生した場合は、大胆に問題を提起する必要があります。このリファクタリングの経験は当分の間ここにあります。不適切な点がある場合は修正してください。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_51568389/article/details/125837149