Webpack4プラグインの実装原理についての簡単な説明

Webpack4プラグインの実装原理についての簡単な説明

序文

wabpack では、ローダーの他にプラグインがコア機能です。プラグインは、webpack の実行中に一連のイベントをブロードキャストします。プラグインはこれらのイベントをリッスンし、webpack API を介して出力ファイルを処理します。たとえば、hmlt-webpack-plugin はテンプレート index.html を dist ディレクトリにコピーします。

知る

まずはソースコードを通してプラグインの基本構造を理解しましょう
https://github.com/webpack/webpack/blob/webpack-4/lib/Compiler.js 551 行目

// コンパイラを作成する createChildCompiler(
  コンパイル、
  コンパイラ名、
  コンパイラインデックス、
  出力オプション、
  plugins // プラグインを含む) {

   // 新しいコンパイラ const childCompiler = new Compiler(this.context);
  // 既存のプラグインをすべて検索 if (Array.isArray(plugins)) {
    for (const plugin of plugins) {
       // 存在する場合は、プラグインの適用メソッドを呼び出します。plugin.apply(childCompiler);
    }
  }
  
  // プラグインに対応するフックをトラバースして見つける
  for (const name in this.hooks) {
    もし (
      ![
        "作る"、
        "コンパイル"、
        「放出」、
        「afterEmit」、
        "無効"、
        "終わり"、
        「このコンピレーション」
      ].includes(名前)
    ){
    
      // 対応するフックを見つけて呼び出します。 
      if (childCompiler.hooks[名前]) {
        childCompiler.hooks[名前].taps = this.hooks[名前].taps.slice();
      }
    }
  }
 
 // .... 省略....

  子コンパイラを返します。
}

上記のソースコードから、プラグインは本質的にクラスであることがわかります。まず、コンパイラクラスが作成され、現在のコンテキストが渡され、それが存在するかどうかが判断されます。存在する場合は、対応するプラグインの apply メソッドが直接呼び出され、対応するプラグインによって呼び出されたフックイベントストリームが見つかり、対応するフックイベントに送信されます。
フックはどこから来るのでしょうか?

https://github.com/webpack/webpack/blob/webpack-4/lib/Compiler.js 42行目

// 上記の Compiler クラスは Tapable クラスを継承しており、Tapable はこれらのフック イベント フローを定義します。class Compiler extends Tapable {
 コンストラクタ(コンテキスト) {
            素晴らしい();
            this.hooks = {
                    /** @type {SyncBailHook<コンパイル>} */
                    shouldEmit: 新しいSyncBailHook(["コンパイル"])、
                    /** @type {AsyncSeriesHook<Stats>} */
                    完了: 新しい AsyncSeriesHook(["stats"])、
                    /** @type {AsyncSeriesHook<>} */
                    追加パス: 新しいAsyncSeriesHook([])、
                    /** @type {AsyncSeriesHook<Compiler>} */
                    beforeRun: 新しいAsyncSeriesHook(["compiler"])、
                    /** @type {AsyncSeriesHook<Compiler>} */
                    実行: 新しい AsyncSeriesHook(["compiler"]),
                    /** @type {AsyncSeriesHook<コンパイル>} */
                    出力: 新しい AsyncSeriesHook(["コンパイル"]),
                    /** @type {AsyncSeriesHook<文字列、バッファ>} */
                    アセットが発行されました: 新しい AsyncSeriesHook(["file", "content"]),
                    /** @type {AsyncSeriesHook<コンパイル>} */
                    afterEmit: 新しいAsyncSeriesHook(["コンパイル"])、

                    /** @type {SyncHook<Compilation, CompilationParams>} */
                    thisCompilation: 新しいSyncHook(["compilation", "params"]),
                    /** @type {SyncHook<Compilation, CompilationParams>} */
                    コンパイル: 新しい SyncHook(["コンパイル", "パラメータ"]),
                    /** @type {SyncHook<NormalModuleFactory>} */
                    normalModuleFactory: 新しいSyncHook(["normalModuleFactory"])、
                    /** @type {SyncHook<ContextModuleFactory>} */
                    contextModuleFactory: 新しいSyncHook(["contextModulefactory"])、

                    /** @type {AsyncSeriesHook<CompilationParams>} */
                    コンパイル前: 新しい AsyncSeriesHook(["params"])、
                    /** @type {SyncHook<CompilationParams>} */
                    コンパイル: 新しいSyncHook(["params"])、
                    /** @type {AsyncParallelHook<コンパイル>} */
                    作成: 新しい AsyncParallelHook(["コンパイル"])、
                    /** @type {AsyncSeriesHook<コンパイル>} */
                    afterCompile: 新しいAsyncSeriesHook(["コンパイル"])、

                    /** @type {AsyncSeriesHook<Compiler>} */
                    watchRun: 新しいAsyncSeriesHook(["compiler"])、
                    /** @type {SyncHook<Error>} */
                    失敗: 新しいSyncHook(["error"])、
                    /** @type {SyncHook<文字列, 文字列>} */
                    無効: 新しいSyncHook(["filename", "changeTime"]),
                    /** @type {SyncHook} */
                    watchClose: 新しいSyncHook([])、

                    /** @type {SyncBailHook<string, string, any[]>} */
                    インフラストラクチャログ: 新しい SyncBailHook(["origin", "type", "args"])、

                    // TODO 次のフックは奇妙な場所にここにあります
                    // TODO webpack 5 用に移動します
                    /** @type {SyncHook} */
                    環境: 新しいSyncHook([])、
                    /** @type {SyncHook} */
                    afterEnvironment: 新しいSyncHook([])、
                    /** @type {SyncHook<コンパイラ>} */
                    afterPlugins: 新しいSyncHook(["compiler"])、
                    /** @type {SyncHook<コンパイラ>} */
                    afterResolvers: 新しいSyncHook(["compiler"])、
                    /** @type {SyncBailHook<文字列, エントリ>} */
                    エントリオプション: 新しい SyncBailHook(["context", "entry"])
            };
            
            // TODO webpack 5 これを削除する
            this.hooks.infrastructurelog = this.hooks.infrastructureLog;
               
            // 対応するコンパイラをタブで呼び出し、コールバック関数を渡します this._pluginCompat.tap("Compiler", options => {
                    スイッチ (オプション.名前) {
                            ケース「追加パス」:
                            「実行前」の場合:
                            ケース「実行」:
                            ケース「emit」:
                            「アフターエミット」の場合:
                            「コンパイル前」の場合:
                            ケース「make」:
                            「コンパイル後」の場合:
                            ケース「watch-run」:
                                    オプション。
                                    壊す;
                    }
            });
            // 以下省略......
  }

さて、基本的な構造を理解した後、プラグインの基本的な構造と使用法を推測することができます。それは次のようになります。

// プラグインクラスを定義する class MyPlugins {
    // 前述のように、新しいコンパイラインスタンスが作成され、そのインスタンスのapplyメソッドが実行され、対応するコンパイラインスタンスが渡されます。apply (compiler) {
        // 新しいコンパイラインスタンスの下でフックイベントフローを呼び出し、タブを通じてそれをトリガーし、コールバック関数を受け取ります。compiler.hooks.done.tap('通常はプラグインのニックネーム'、(デフォルトの受信パラメータ) => {
            console.log('実行本体を入力してください');
        })
    }
}
// エクスポートモジュール.exports = MyPlugins

上記は単純なテンプレートです。内部フック関数を試して、期待どおりに呼び出されてトリガーされるかどうかを確認しましょう。

webpackを設定する

path = require('path') とします。
DonePlugin = require('./plugins/DonePlugins') とします。
AsyncPlugins = require('./plugins/AsyncPlugins') とします。

モジュール.エクスポート = {
    モード: '開発'、
    エントリ: './src/index.js',
    出力: {
        ファイル名: 'build.js',
        パス: path.resolve(__dirname, 'dist')
    },
    プラグイン: [
        new DonePlugin(), // 内部同期フック
        new AsyncPlugins() // 内部非同期フック
    ]
}

同期プラグイン プラグインシミュレーション呼び出し

クラス DonePlugins {
    適用(コンパイラ){
        コンパイラーフックの完了.tap('DonePlugin', (統計) => {
            console.log('実行: コンパイルが完了しました');
        })
    }
}

module.exports = 完了プラグイン

非同期プラグイン プラグイン シミュレーション呼び出し

クラスAsyncPlugins {
    適用(コンパイラ){
        コンパイラーフックのemit.tapAsync('AsyncPlugin', (complete, callback) => {
            タイムアウトを設定する(() => {
                console.log('実行: ファイルが出力されました');
                折り返し電話()
            }, 1000)
        })
    }
}

module.exports = 非同期プラグイン

最後に、webpack をコンパイルすると、コンパイル コンソールが表示され、次のように出力されて実行されます: コンパイルが完了しました。実行: ファイルが発行され、フック イベント フローを呼び出してトリガーできることが示されます。

練習すれば完璧になる

基本的な構造と使い方がわかったので、プラグインを書いてみましょう。ファイル記述プラグインを書いてみましょう。日常のパッケージングでは、xxx.md ファイルを dist ディレクトリにパッケージ化し、パッケージ記述を作成することで、このような小さな機能を実現できます。

ファイル説明プラグイン

クラスFileListPlugin {
    // 初期化、ファイル名を取得するコンストラクタ ({filename}) {
        this.filename = ファイル名
    }
    // 同じテンプレート形式で、apply メソッドを定義します。apply (compiler) {
        コンパイラー.フック.emit.tap('FileListPlugin', (コンパイル) => {
            // アセットは静的リソースであり、コンパイルパラメータを出力でき、多くのメソッドとプロパティがあります。let アセット = compilation.assets;
            
            // 出力ドキュメント構造を定義します let content = `## ファイル名 リソース サイズ\r\n`
            
            // 静的リソースを走査し、出力コンテンツを動的に結合します Object.entries(assets).forEach(([filename, stateObj]) => {
                コンテンツ += `- ${filename} ${stateObj.size()}\r\n`
            })
            
            // 出力リソースオブジェクトassets[this.filename] = {
                ソース () {
                    コンテンツを返します。
                },
                サイズ () {
                    コンテンツの長さを返す
                }
            }
            
        })
    }
}
// エクスポート module.exports = FileListPlugin

webpack の設定

path = require('path') とします。
HtmlWebpackPlugin = require('html-webpack-plugin') を設定します。
// プラグインディレクトリは、node_modules、カスタムプラグインと同じレベルにあり、ローダーに似ています。let FileListPlugin = require('./plugins/FileListPlugin')

モジュール.エクスポート = {
    モード: '開発'、
    エントリ: './src/index.js',
    出力: {
        ファイル名: 'build.js',
        パス: path.resolve(__dirname, 'dist')
    },
    プラグイン: [
        新しいHtmlWebpackプラグイン({
            テンプレート: './src/index.html',
            ファイル名: 'index.html'
        })、
        新しいファイルリストプラグイン({
            ファイル名: 'list.md'
        })
    ]
}

上記の設定により、再度パッケージ化すると、パッケージ化されるたびに dist ディレクトリに xxx.md ファイルが表示され、このファイルの内容が上記の内容になることがわかります。

Webpack4プラグインの実装原理に関するこの記事はこれで終わりです。Webpack4プラグインに関するその他の関連コンテンツについては、123WORDPRESS.COMの過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも123WORDPRESS.COMをよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Web 開発 js 文字列連結プレースホルダーと conlose オブジェクト API の詳細な説明
  • Web プロジェクト開発 JS 機能の手ぶれ補正とスロットリングのサンプル コード
  • ウェブ開発におけるクロスドメインの理由に対する複数のソリューション
  • ウェブメッセージボード機能を実現するjs
  • JavaScriptの記事では、Webフォームの操作方法を説明します。
  • JavaScript ウェブページ入門開発詳細説明

<<:  MySQLの比較演算子正規表現マッチングREGEXPの使用の詳細な説明

>>:  Ubuntuサーバーの一般的なコマンドの概要

推薦する

Linux crontab コマンドの使用

1. コマンドの紹介contab (cron テーブル) コマンドは、Windows のスケジュール...

JavaScript のドキュメント オブジェクト モデル (DOM)

目次1. DOMとは何か2. 要素を選択する3. getElementById() 4. クエリセレ...

Yahooが開発したウェブページスコアリングプラグインYSlowのスコアリングルール

YSlow は、Yahoo USA が開発したページ スコアリング プラグインです。非常に優れていま...

Vueでスワイパープラグインを使用する際の問題を解決する

デモを作成するときにこのプラグインを使用していくつか問題が発生したため、プラグインの使用方法といくつ...

MySQL GRANT ユーザー認証の実装

承認とは、ユーザーに特定の権限を付与することです。たとえば、新しく作成したユーザーに、すべてのデータ...

集める価値のある 15 個の JavaScript 関数

目次1. 数字を逆にする2. 配列内の最大のn個の数値を取得する3. 階乗を計算する4. 現在の動作...

JS の querySelector メソッドと getElementById メソッドの違い

目次1. 概要1.1 querySelector() と querySelectorAll() の使...

Linux システムでの CPU 使用率が高い場合のトラブルシューティングのアイデアと解決策

序文Linux 運用保守エンジニアとして、日々の業務の中で Linux サーバーの CPU 負荷が ...

Python スクリプトを Ubuntu で直接実行する方法

翻訳プログラムを例に挙げてみます。前回はWindowsでのアプリケーションのパッケージ化についてお話...

Flash での HTML と CSS の適用

Flash での HTML と CSS の適用:同僚の Den が Flash で HTML と C...

CSS継承方法

次の背景画像を持つ div があるとします。 次の反射効果を作成します。 方法はたくさんありますが、...

Docker ベースの MySQL マスタースレーブ レプリケーションを実装する方法

序文MySQL マスター/スレーブ レプリケーションは、アプリケーションの高パフォーマンスと高可用性...

MySQL の一般的なログの概要

序文: MySQL システムには、さまざまな種類のログが存在します。さまざまなログにはそれぞれ独自の...

Linux システムの .bash_profile ファイルの詳細な説明

目次1. 環境変数$PATH: 2. 環境変数を変更します。 3. bash_profileの目的要...

シンプルなCSSアニメーションのtransition属性の詳しい説明

1. 遷移属性の理解1. transition 属性は、次の 4 つの遷移プロパティを設定するために...