プライベートな連絡先の最初の情報
送料メール:
2024-07-08
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
主なプロセスに加えて、いくつかのあいまいな概念についても詳しく説明します。
前に 初期化、作成、封印「」では、Webpack の基礎となる構築ロジックが次のように大別できることを紹介しました。初期化、構築、パッケージ化「3つの段階:
で、"構築する「このフェーズは、モジュール間の依存関係を分析し、 依存関係グラフ(モジュールグラフ); 次に、「カプセル化」の段階では、依存関係グラフに基づいてモジュールが複数の Chunk オブジェクトに個別にカプセル化され、Chunk 間の親子依存関係が ChunkGraph と複数の ChunkGroup オブジェクトに分類されます。
「カプセル化」フェーズの最も重要な目標は、「構築」フェーズで収集された ModuleGraph 関係グラフに基づいて ChunkGraph 関係グラフを構築することです。このプロセスのロジックは比較的複雑です。
ここで、いくつかの重要なステップの実装ロジックを簡単に分析してみましょう。
最初のステップは非常に重要です。 移行seal()
関数の後、トラバースしますentry
設定、エントリごとに空の設定を作成しますChunk
そしてエントリーポイント オブジェクト (特別なChunkGroup
)、最初に基本的な設定を行います。 ChunkGraph
構造的な関係、次のステップの準備、キーコード:
class Compilation {
seal(callback) {
// ...
const chunkGraphInit = new Map();
// 遍历入口模块列表
for (const [name, { dependencies, includeDependencies, options }] of this
.entries) {
// 为每一个 entry 创建对应的 Chunk 对象
const chunk = this.addChunk(name);
// 为每一个 entry 创建对应的 ChunkGroup 对象
const entrypoint = new Entrypoint(options);
// 关联 Chunk 与 ChunkGroup
connectChunkGroupAndChunk(entrypoint, chunk);
// 遍历 entry Dependency 列表
for (const dep of [...this.globalEntry.dependencies, ...dependencies]) {
// 为每一个 EntryPoint 关联入口依赖对象,以便下一步从入口依赖开始遍历其它模块
entrypoint.addOrigin(null, { name }, /** @type {any} */ (dep).request);
const module = this.moduleGraph.getModule(dep);
if (module) {
// 在 ChunkGraph 中记录入口模块与 Chunk 关系
chunkGraph.connectChunkAndEntryModule(chunk, module, entrypoint);
// ...
}
}
}
// 调用 buildChunkGraph 方法,开始构建 ChunkGraph
buildChunkGraph(this, chunkGraphInit);
// 触发各种优化钩子
// ...
}
}
実行が完了すると、次のデータ構造が形成されます。
次に、この時点で構成されている場合、 entry.runtime
, Webpack はこの段階でランタイム コードも提供します。 作成する 対応するチャンクと直接配布する 与えるentry
対応するChunkGroup
物体。すべての準備ができたときに呼び出されます チャンクグラフの構築 機能がある場合は、次のステップに進みます。
ステップ2: 存在するbuildChunkGraph
関数内移行 visitModules
関数では、ModuleGraph を走査し、依存関係に従ってすべてのモジュールを異なるモジュールに割り当てます。Chunk
このプロセス中にオブジェクトが見つかった場合非同期モジュール、次にモジュール 作成する新しい ChunkGroup
そしてChunk
オブジェクトであり、最終的には次のデータ構造を形成します。
3番目のステップ: 存在するbuildChunkGraph
機能中移行 connectChunkGroups
方法、確立するChunkGroup
間、Chunk
完全な相互依存関係を生成するChunkGraph
オブジェクトであり、最終的には次のデータ構造を形成します。
4番目のステップ: 存在するbuildChunkGraph
機能中移行 cleanupUnconnectedGroups
メソッド、クリーンアップが無効ですChunkGroup
、主にパフォーマンスの最適化に役割を果たします。
この4つのステップを上から下まで経て、ModuleGraph
に格納されたモジュールは、モジュール自体の性質に応じて Entry、Async、Runtime の 3 つの異なる Chunk オブジェクトに割り当てられ、チャンク間の依存関係は ChunkGraph コレクションと ChunkGroup コレクションに格納されます。これらのオブジェクトに基づいて続行できます。後で下請けポリシーを変更します (例:SplitChunksPlugin
)、Module オブジェクトと Chunk オブジェクトの所有権を再編成して割り当てることで、下請けの最適化を実現します。
上記の構築プロセスには、Chunk、ChunkGroup、および ChunkGraph という 3 つの主要なオブジェクトが含まれます。まず、理解を深めるために、それらの概念と機能を要約します。
Chunk
: モジュールは、モジュールのコンテンツの読み取り、モジュール間の依存関係の記録などに使用されます。一方、チャンクは、モジュールの依存関係に基づいて複数のモジュールをマージし、それらをアセット ファイルに出力します (プロダクトのマージと出力のロジックについては、次の章で説明します)。ChunkGroup
:1つ ChunkGroup
1 つ以上が含まれていますChunk
物体;ChunkGroup
そしてChunkGroup
親子依存関係は、以下の間で形成されます。ChunkGraph
: 最後に、Webpack はチャンクとチャンクグループの間の依存関係を次の場所に保存します。 compilation.chunkGraph
オブジェクト内では、次の型関係が形成されます。上記 ChunkGraph
ビルド プロセスでは、最終的にモジュールが 3 つの異なるタイプのチャンクに編成されます。
entry
下のタッチで到達したモジュールはチャンクに編成されます。entry.runtime
空でない場合、ランタイム モジュールはチャンクに編成されます。使用しない場合、これは Webpack に組み込まれます splitChunks
他のプラグインの場合、モジュールの入力を出力にマッピングするためのデフォルトのルールは、Webpack の基礎となる重要な原則の 1 つであるため、各チャンクの特定のルールを導入する必要があります。
エントリ チャンク:
エントリ チャンクから始めて、Webpack はまず次のことを行います。 entry
作成するChunk
オブジェクト、たとえば次の構成:
module.exports = {
entry: {
main: "./src/main",
home: "./src/home",
}
};
トラバース entry
オブジェクトのプロパティと作成chunk[main]
、chunk[home]
2 つのオブジェクト。この時点では、2 つのチャンクにはそれぞれ次のものが含まれています。main
、home
モジュール:
初期化が完了すると、Webpack は ModuleGraph
依存関係データ、entry
以下によってタッチされるすべてのモジュールはチャンクに詰め込まれます (訪問モジュール メソッド)、たとえば、次のファイル依存関係の場合:
main.js
4 つのファイル a/b/c/d が直接的または間接的に同期的に参照される場合、Webpack は最初にmain.js
このモジュールは Chunk オブジェクトと EntryPoint オブジェクトを作成し、a/b/c/d モジュールを段階的に追加します。chunk[main]
、最終的に形成されるもの:
非同期チャンク:
次に、Webpack は各非同期インポート ステートメントをコンパイルします (import(xxx)
そしてrequire.ensure
) は別個のチャンク オブジェクトとして処理され、そのすべてのサブモジュールがこのチャンクに追加されます。これを非同期チャンクと呼びます。たとえば、次の例の場合:
// index.js
import './sync-a.js'
import './sync-b.js'
import('./async-a.js')
// async-a.js
import './sync-c.js'
エントリーモジュール内 index.js
sync-a と sync-b は同期的に導入され、async-a では同時に async-a モジュールが同期的に導入されます。sync-c
モジュールを作成し、次のモジュール依存関係図を形成します。
この時点では、Webpack がエントリ ポイントになります。 index.js
、非同期モジュール async-a.js
サブパッケージを個別に作成して、次のチャンク構造を形成します。
そして chunk[index]
そしてchunk[async-a]
それらの間には一方向の依存関係が形成され、Webpack はこの依存関係を次の場所に保存します。ChunkGroup._parents
、ChunkGroup._children
プロパティで。
ランタイム チャンク:
最後に、例外として、 entry
, Webpack5 では、非同期モジュールに加えて、ランタイム コードをチャンクに分けて抽出することもサポートしています。ここで説明するランタイム コードは、パッケージ化された製品が正常に実行できるようにするために Webpack によって挿入される一連の基本的なフレームワーク コードを指します。たとえば、一般的な Webpack のパッケージ化された製品の構造は次のとおりです。
上の図の赤いボックスで囲まれたコードの大部分は、Webpack によって動的に生成されたランタイム コードです。コンパイル時に、Webpack はビジネス コードに基づいてどのランタイム コードを出力するかを決定します。Dependency
サブクラス)、例:
__webpack_require__.f
、__webpack_require__.r
および最小限のモジュールサポートを実現するその他の機能。__webpack_require__.e
関数;__webpack_require__.o
関数;それぞれのランタイム コードは小さいかもしれませんが、機能が増加するにつれて、最終的な結果はますます大きくなり、特に複数のエントリを持つアプリケーションの場合、この Webpack5 が提供する同じようなランタイムを繰り返しパッケージ化するのは少し無駄です。 entry.runtime
構成項目は、ランタイム コードがどのようにパッケージ化されるかを宣言するために使用されます。使い方としては、そのまま使用してくださいentry
項目に文字列フォームを追加するruntime
値、たとえば:
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
}
};
存在する compilation.seal
この関数では、Webpack の最初は次のとおりです。entry
作成するEntryPoint
、その後判断します entry
構成には以下が含まれますか?runtime
プロパティがある場合は、次のように作成します。runtime
値はチャンクの名前です。したがって、上記の設定例では 2 つのチャンクが生成されます。chunk[index.js]
、chunk[solid-runtime]
これに基づいて、最終的に 2 つのファイルが出力されます。
index.js
書類;solid-runtime.js
書類。多くの中 entry
シナリオの中で、それぞれにentry
全て同じ設定にruntime
この値を設定すると、Webpack ランタイム コードがマージされて同じランタイム チャンクに書き込まれ、最終的に製品パフォーマンスの最適化が達成されます。たとえば、次の構成の場合:
module.exports = {
entry: {
index: { import: "./src/index", runtime: "solid-runtime" },
home: { import: "./src/home", runtime: "solid-runtime" },
}
};
入り口 index
、home
同じものを共有するruntime
値を指定し、最後に 3 つのチャンクをそれぞれ生成します。
この時の入場 chunk[index]
、chunk[home]
ランタイムありchunk[solid-runtime]
親子の依存関係も形成されます。
デフォルトのサブパッケージ化ルールの最大の問題は、複数のチャンクに同じモジュールが同時に含まれている場合、このモジュールが制限なくこれらのチャンクに繰り返しパッケージ化されることです。たとえば、両方とも同じモジュールに依存する 2 つのエントリ main/index があるとします。
デフォルトでは、Webpack はこれに対して追加の処理を行わず、単純に c モジュールを main/index 2 つのチャンクに同時にパッケージ化し、最終的に次のように形成します。
見られます chunk
モジュール c が互いに分離されていると、モジュール c が繰り返しパッケージ化され、最終製品に不必要なパフォーマンスの損失が発生する可能性があります。
この問題を解決するために、Webpack 3 が導入されました。 CommonChunkPlugin
プラグインは、エントリ間の共通の依存関係を別のファイルに抽出しようとします。chunk
、しかし CommonChunkPlugin
これは基本的に、チャンク間の単純な親子関係チェーンに基づいて実装されます。抽出された 3 番目のパッケージを次のように使用する必要があると推測するのは困難です。entry
父親chunk
まだ子供だよchunk
,CommonChunkPlugin
親としての統合処理chunk
、場合によっては、パフォーマンスに重大な悪影響を及ぼします。
このため、Webpack 4 以降では、より複雑なデータ構造が特別に導入されました— ChunkGroup
リレーションシップチェーンの管理と連携の実現に特化SplitChunksPlugin
より効率的かつインテリジェントに実装できるヒューリスティックな下請け。
要約すると、「ビルド」フェーズはモジュールの参照関係に基づいて ModuleGraph を構築する役割を果たし、「カプセル化」フェーズは ModuleGraph に基づいて一連のチャンク オブジェクトを構築し、チャンク間の依存関係 (非同期参照、ランタイム) を整理する役割を担います。 ) ChunkGraph - チャンク依存関係グラフ オブジェクトに変換します。 ModuleGraph と同様に、ChunkGraph 構造の導入により、チャンク間の依存関係の管理ロジックを分離することもでき、全体的なアーキテクチャ ロジックがより合理的で拡張しやすくなります。
ただし、非常に複雑に見えますが、「パッケージ化」段階の最も重要な目標は、チャンクの数と各チャンクにどのモジュールが含まれるかを決定することです。これらは、最終的なパッケージ化の結果に実際に影響を与える重要な要素です。
この点については、Webpack5 に組み込まれている 3 つのサブコントラクト ルール、Entry Chunk、Async Chunk、Runtime Chunk を理解する必要があります。これらは、他のプラグイン (たとえば、 分割チャンクプラグイン) はすべてこれに基づいており、次の助けを借りています。 buildChunkGraph
後でトリガーされるさまざまなフックにより、チャンク構造がさらに分割、マージ、最適化され、拡張された下請け効果が実現されます。
考える Chunk
製品ファイルは 1 つだけ作成されますか?なぜ?mini-css-extract-plugin
、file-loader
追加ファイルを書き込むことができるこの種のコンポーネントは、下部にどのように実装されているのでしょうか?