webpackを使用したことがある場合は、おそらくwebpackプラグインについて聞いたことがあるでしょう。 プラグインは、webpackの実装とアーキテクチャを拡張するための優れたオプションです。 webpackのソースコードを見ると、約80% ofのコードがプラグインを使用して実装されています。 これは、webpackのコア部分を分離するのに役立ち、コードのメンテナンスが向上します。

webpackは、loadersの概念もサポートしており、webpackの拡張にも役立ち、resolversと連携します。 これらは主にソースコードを変換するために使用されます。 カバーするのは別のトピックであり、カスタムローダーを作成する方法についての記事もすぐに書くでしょう。

カスタムWebpackプラグインの作成について詳しく説明する前に、モジュールバンドラーの基本的な動作と、Webpackが内部でどのように機能するかを知る必要があります。 基本的なモジュールバンドラーの目標は、ソースコードを読み取り、依存関係を見つけることです。これは、依存関係の解決と呼ばれます。

依存関係の解決中に、バンドラーはモジュールマッピング(モジュールマップ)を実行し、それらを1つのファイルにバンドルし、1つのモジュールにパッケージ化します。 webpackはこれらの部分を高度な方法で実行し、効率的にするために他のいくつかの手順も追加します。 次の手順を使用して、webpackのアーキテクチャを破ることができます。

  • コンパイラ:トップレベルのAPIがあり、webpackの実行を制御するためのフックを提供します。
  • コンパイルまたは依存関係グラフ:コンパイラーによって返され、依存関係グラフの作成を開始します。
  • Resolver :提供されたエントリパスの絶対パスを作成し、結果、リクエスト、パス、コンテキストなどの詳細を返します。
  • Parser :AST(抽象構文木)を作成し、requiresやimportsなどの興味深いものを探して、依存関係オブジェクトを作成します。
  • モジュールファクトリ:これらのオブジェクトはmoduleFactory関数に渡され、モジュールを作成します。
  • Templates :モジュールオブジェクトのデータバインディングを実行し、バンドルされたファイルにコードを作成します。

webpackは、コンパイラ、パーサー、およびコンパイル用のフックを提供します。 tapable と呼ばれるライブラリを使用します。これは、webpackチームによって維持され、メソッドを利用できる強力で強力なフックの作成に役立ちます。

フックとタッピングインメソッドとは何ですか?

フックはイベントに似ており、フックを利用することは、イベントをリッスンして適切なメソッドを実行するリスナーのようなものです。 たとえば、次のようなDOM関連のイベントリスナーを配置する場合:

window.addEventListener('load', (event) => {
  loadEventListerner(event)
});

この場合、loadは、loadEventListenerが利用しているイベントまたはフックです。

webpackはどのようにテープを使用し、プラグインはどのように画像に組み込まれますか?

実際の例を見て、webpackがどのようにテーピングを使用するかを説明しましょう。 ピザ配達アプリからピザを注文しているとしましょう。 これで、メニューの確認、注文のカスタマイズ、そして最終的に料金を支払って注文するなど、プロセスに関連する一連のステップがあります。 これから先、ピザが届くまで、アプリは注文の進行状況に関する通知を送信します。

この例では、ピザ配達アプリを webpack に、自分自身を webpackプラグインに、通知をtapableによって作成されたフックに置き換えることができます。

webpackは、コンパイラコンパイルパーサーステージのフックをtaableを使用して作成し、プラグインはそれらをタップまたはリッスンしてそれに応じて動作します。

これらの理論と概念を十分に理解して、コードを見せてください。

この投稿では、作成されたバンドルファイルのサイズをチェックし、サイズ制限に基づいてエラーまたは警告をログに記録する単純なwebpackプラグインを作成します。 これらのサイズ制限はプラグインオプションでも渡すことができ、デフォルトのサイズ制限は3KBのままにします。 したがって、出力ファイルプラグインがサイズ制限を超えている場合は常にエラーメッセージをログに記録し、それを下回っている場合は安全なメッセージをログに記録し、サイズ制限と等しい場合はユーザーに警告します。

プラグインのコードはここにあります。

最初にプロジェクトをセットアップしましょう。

プロジェクトディレクトリで、npmまたはYarnを使用してwebpackをインストールします。

$ npm init -y
$ npm install webpack webpack-cli --save-dev

この後、index.jsファイルを含むsrcディレクトリを作成します。ここで、入力パスまたはエントリパスがポイントし、プロジェクトのルートディレクトリにwebpack.config.jsファイルを作成します。

これで、プラグインのディレクトリを作成し、bundlesize-webpack-pluginのような名前を付けて、そのディレクトリ内にindex.jsを作成できます。

プロジェクト構造は次のようになります。

webpack-Plugin-demo-directory
  |- webpack.config.js
  |- package.json
  |- /src
    |- index.js
  |- /bundlesize-webpack-plugin
    |- index.js

package.jsonファイルのscriptsに次のビルドスクリプトを追加します。

"build": "webpack"

そして、bundlesize-webpack-plugin/index.jsに次のコードを記述します。

module.exports = class BundlesizeWebpackPlugin {
 constructor(options) {
   this.options = options;
 }
 apply(compiler) {
   console.log("FROM BUNDLESIZE PLUGIN");
 }
};

これについてはすぐに説明します。

webpack.config.jsに、次のコードを記述します。

const { resolve } = require("path");
const bundlesizeplugin = require("./bundlesize-webpack-plugin");

module.exports = {
 entry: resolve(__dirname, "src/index.js"),
 output: {
   path: resolve(__dirname, "bin"),
   filename: "bundle.js"
 },
 plugins: [new bundlesizeplugin()]
};

次に、npm run buildを実行します。

ターミナルに「FROMBUNDLESIZEPLUGIN」というメッセージが表示されます。

これで、webpackプラグインが作成されました。

それを分解する

すべてのwebpackプラグインには、webpackによって呼び出されるapplyメソッドが含まれている必要があり、webpackはそのメソッドの引数としてコンパイラインスタンスを提供します。

プラグインは、クラスベースにすることも、関数ベースにすることもできます。 プラグインが関数ベースの場合、関数の引数もコンパイラです。 この記事ではクラスベースを使用することをお勧めします。

Webpackのソースコードとがどのように実装されているかをここで確認できます

クラスのコンストラクターには、options引数があることがわかります。 これは、プラグインがいくつかのオプションを受け入れる場合に使用されます。 オプションとしてsizeLimitを渡します。渡されない場合、デフォルトは3KBになります。

これで、コンストラクターメソッドを次のように変更できます。

constructor(options) {
   this.options = options || {
     sizeLimit: 3
   };
 }

次のように、sizeLimitをプラグインoptionsとして渡すこともできます。

plugins: [
   new bundlesizeplugin({
     sizeLimit: 4
   })
 ]

webpack.config.jsでは、エントリポイントについて言及し、bundle.jsファイルのbinという名前のフォルダーにバンドルファイルを出力するようにwebpackに指示し、プラグインを使用するようにwebpackに指示しています。 bundlesize-webpack-pluginフォルダーから。

プロジェクトの準備ができたので、アセットサイズを確認し、sizeLimitと比較してみましょう。 コンパイル作業が完了し、バンドルされたファイルが生成されるときに発行されるcompiler.hooks.doneフックを使用します。 バンドルされたファイルの詳細は、この方法で取得できます。

非同期のフックがいくつかあり、それらに非同期のタッピング方法を使用できることに注意してください。 ここでこれらについて学ぶことができます

apply(compiler) {
   compiler.hooks.done.tap("BundleSizePlugin", (stats) => {
     const {
        path,
        filename
     } = stats.compilation.options.output;
   })
 }

ここでは、コンパイラの doneフックまたはイベントにtappingします。メソッドの最初の引数は、webpackが参照に使用するプラグイン名であり、2番目のメソッドはstatsを引数として取るコールバック。 console.log(stats)を使用して統計の内容を確認できます。これにより、コンパイルとそのフックで使用可能なファイルに関するすべての可能な詳細を含む大きなオブジェクトが表示されます。 出力プロパティからパスとファイル名を抽出しています。 これからは、Node.jsのコアライブラリpathおよびfsモジュールを使用してファイルの詳細を取得するだけです。

apply(compiler) {
   compiler.hooks.done.tap("BundleSizePlugin", stats => {
     const { path, filename } = stats.compilation.options.output;
     const bundlePath = resolve(path, filename);
     const { size } = fs.statSync(bundlePath);
     console.log(size); // size in bytes
   });
 }

簡単でしょ?

これで、 this StackOverflow answer のような関数を使用して、サイズをバイトからkbに変換できます。

次に、sizeLimitおよびconsole.logの適切なメッセージと比較します。

apply(compiler) {
   compiler.hooks.done.tap("BundleSizePlugin", stats => {
     const { path, filename } = stats.compilation.options.output;
     const bundlePath = resolve(path, filename);
     const { size } = fs.statSync(bundlePath);
     const { bundleSize, fullSizeInfo } = formatBytes(size);
     const { sizeLimit } = this.options;
     if (bundleSize < sizeLimit) {
       console.log(
         "Safe:Bundle-Size",
         fullSizeInfo,
         "\n SIZE LIMIT:",
         sizeLimit
       );
     } else {
       if (bundleSize === sizeLimit) {
         console.warn(
           "Warn:Bundle-Size",
           fullSizeInfo,
           "\n SIZE LIMIT:",
           sizeLimit
         );
       } else {
         console.error(
           "Unsafe:Bundle-Size",
           fullSizeInfo,
           "\n SIZE LIMIT:",
           sizeLimit
         );
       }
     }
   });
 }

それでおしまい! これで、バンドルサイズをチェックし、サイズ制限に基づいてレポートする独自のWebpackプラグインができました。

これをnpmレジストリで公開できるようになりました。

webpackがプラグインに効果的であると考える標準はほとんどありません。 webpack-defaultを出発点として使用できます。

私がすでに公開しているbundlesize-webpack-pluginも独自のフックを拡張しており、それらはテーピングを使用して作成されていることに注意してください。 実装はmasterブランチにあります。

概要

  • Webpackが内部でどのように機能し、そのアーキテクチャがどのように実装されているかを確認しました
  • フックとそれを利用することの意味について学びました
  • プラグインがシステムにどのように組み込まれるかを見ました
  • バンドルされたファイルのサイズをチェックするための簡単なプラグインを作成しました