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サーバーの一般的なコマンドの概要

推薦する

JavaScriptはシンプルな計算機能を実装します

この記事では、参考までに、簡単な計算機を実装するためのJavaScriptの具体的なコードを紹介しま...

jsとcssのブロッキング問題の詳細な分析

目次DOMContentLoadedとロードjs ブロッキングとは何ですか? CSS ブロッキングと...

HTMLチュートリアル、簡単に学べるHTML言語

1. <body background=画像ファイル名 bgcolor=color text=...

Linux でスレッドを作成するための pthread_create の具体的な使用法

pthread_create関数機能紹介pthread_createはUNIX環境のスレッド作成関数...

Oracle VM VirtualBox の CentOS7 オペレーティング システムのインストール チュートリアル図

目次インストール手順環境設定実行構成インストール手順ダウンロードアドレス: バージョン6.0 最初に...

Linux sar コマンドの使用方法とコード例の分析

1. CPU使用率sar -p (一日中表示) sar -u 1 10 (1: 1秒ごと、10: 1...

Nginx の break と last の違いの詳細な分析

まずは違いについて話しましょう最後に、書き換えられたルールは、次の場所と一致させるために書き換えられ...

Ubuntu 20.04 LTS で Java 開発環境を構成する

Java開発キットjdkをダウンロードするJDK のダウンロード アドレスはhttp://www.o...

Javascriptを使用して滑らかな曲線を生成する方法

目次序文ベジェ曲線の紹介二次ベジェ曲線3次ベジェ曲線ベジェ曲線計算機能フィッティングアルゴリズム付録...

MySQL マルチテーブル結合クエリ例の説明

実際のプロジェクトでは、複数のテーブル間に関係が存在します。 1 つのテーブル内のすべてのデータを取...

VueはElement el-uploadコンポーネントを使用してピットに足を踏み入れます

目次1. 基本的な使い方2. 画像量の制御3. 画像形式の制限/複数の画像を選択可能補足: vueプ...

Vueのコンポーネントのprops属性について詳しく説明します

目次質問1: 小道具は具体的にどのように使用されますか?原理は何ですか?下を見る質問 2: 年齢に ...

vmware14Pro で Ubuntu システム インターフェイスが小さすぎる問題の解決方法の詳細な説明

1. 動作環境vmware14proウブントゥ 16.04LTS 2. 問題の説明vmware14P...

HTML DOCTYPEの略語

DOCTYPE が次のとおりである場合:コードをコピーコードは次のとおりです。 <!DOCTY...

MySQL マスタースレーブレプリケーションの実装手順

目次mysql マスタースレーブレプリケーションMySQL マスタースレーブレプリケーション方式My...