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

推薦する

HTML構造化実装方法

DIV+css構造 CSSレイアウトを学んでいますか?まだ純粋な CSS レイアウトを完全に習得でき...

Nginx をインストールして複数のドメイン名を設定する方法

Nginx のインストールCentOS 6.x yum にはデフォルトで nginx ソフトウェア ...

MySQL サーバー IO 100% 分析および最適化ソリューション

序文ストレス テスト中に、リソース使用のボトルネックによって発生する最も直接的なパフォーマンスの問題...

Vue Element-ui テーブルはツリー構造テーブルを実現します

この記事では、ツリー構造テーブルを実現するためのElement-uiテーブルの具体的なコードを参考ま...

ランダム点呼 Web ページを実装するための JavaScript

JavaScriptは、参考のためにランダムな点呼Webページを作成します。具体的な内容は次のとお...

MySQLでカーソルトリガーを使用する方法

カーソル選択クエリによって返される行のセットは、結果セットと呼ばれます。結果セット内の行は、入力した...

入力ボックスのプレースホルダーテキストのデフォルトの色を変更する -webkit-input-placeholder メソッド

HTML5 では、入力用のネイティブ プレースホルダー属性が追加されており、これは高度なブラウザでサ...

Nginx ポート競合を解決するトラブルシューティング方法の例

問題の説明データ転送に Nginx を使用し、フロントエンドとバックエンドが分離された Spring...

NODE.JS を使用して WEBSERVER を作成する手順

目次Node.jsとはNodeJSをインストールするNode を使用して Hello World を...

jsはキャンバスに基づいて時計コンポーネントを実装します

圧縮アップロード画像、スクラッチカード、ポスター作成、チャートプラグインなど、フロントエンド開発にお...

ページング効果を実装するミニプログラム

この記事の例では、ページング効果表示を実現するためのミニプログラムの具体的なコードを参考までに共有し...

JavaScriptでシンプルなスクロールウィンドウを実装する

この記事では、スクロールウィンドウを実装するためのJavaScriptの具体的なコードを参考までに紹...

MySQLクエリの文字セットの不一致の問題を解決する方法

問題を見つける最近、仕事で問題が発生しました。MySQL データベースにテーブルを作成するときに、ラ...

{{ }} で関数を直接使用する WeChat アプレットの例

序文WeChat アプレット開発 (ネイティブ wxml、wxcss) で、{{ }} 内で直接メソ...

divとtableの選択と組み合わせ方について簡単に説明します

ページレイアウトは、Web ページを扱い始めた頃からずっと気にかけていたことです。初期のテーブル構造...