0からプラグインを書き込みを開始するのWebPACK

1.はじめに

プラグイン(プラグイン)があるwebpackファーストクラスの英雄。多くのウィジェットが行うがあるからだwebpack全能。ではwebpack、ソースコードも内蔵プラグイン、プラグインの多くを使用して、よく使用する場合は、より少ない効率的に作業を行うことができます。とてもクールプラグと他の誰かによって書かれたので、なぜ私たちは、独自のプラグインを書く考慮しませんか?この記事では、どのように独自の記述方法をお教えしますwebpackプラグイン。

2. WebPACKの操作機構

プラグインを書く前に、私たちは最初に見る必要がありwebpack、全体的な操作機構。Webpack操作機構の性質上のイベントの流れのための機構でwebpack操作、初期化パラメータ、ファイルをコンパイル開始、エントリを決定する、モジュールをコンパイルし、コンパイルされ、リソースを生成し、エクスポートリソース、等、一連の処理を完了し、中各ノードのこの全体のプロセスは、webpack我々はによって適切なタイミングで、各イベントのために聞くことができ、ブロードキャストイベント外となりますwebpackで提供API正しいことを行います。

説明を理解しやすいです。

あなたは上記のテキストを理解していない場合は、ユーザーフレンドリーなプラスの私たちの例を説明するために、次のように:

私たちは、webpackコックを比較し、入れてwebpack揚げた料理、シルクと呼ば辛くて酸っぱいポテト料理を調理するために比較して全体の演算処理を。野菜- -クッキング-料理野菜:まあ、皿から皿に次の手順の合計を通過する程度の良い一品を行う準備ができています。そして、次のプロセスのそれぞれは、この手順の名前から彼の大きな叫びましょうときに我々は料理が上手、シェフと合意した、それは、そのような野菜が起動するときに料理として、ブロードキャストイベントを調理することで、シェフが叫んだてみましょう: 「野菜。」他の要件が存在しない場合は、上記の4つのステップを通過し、このチャネル酸っぱいジャガイモのワイヤーは、後に行うことができます。しかし、我々は辛くて酸っぱいポテトのシルクロードにもっと辛いのいくつかを聞かせすることがある追加の要件を、持っています。元シェフが行うには、通常の手順に従いますし、それを行うにはどのようにして、余分なコショウを追加していないのだろうか?この時、私たちのプラグインがそれをデビュー、我々は「プラスペッパー」プラグインを作成する必要があります。これを書く前に、私たちは私たちがどのような手順を監視することを、どのような時にプラス唐辛子適切に考慮する必要がある、料理に追加する場合、ことは明らかです。その後、我々は、我々は唐辛子を入れたとき、すなわち「料理」を叫んで、このイベントのシェフ放送後に、このプロセスを「調理する」聞く最後はそれより辛い辛くて酸っぱいジャガイモのワイヤーは、いくつかのものであろうを行うためになるように、追加。

要約すると:

Webpack生産ラインと同様に、処理の一連のプロセスを経て出力するソースファイルを変換します。
このプロセスの職務の各々は、単一の生産ラインであり、複数のプロセス間の依存関係が存在し、そして現在のプロセスに対処するために、次の工程にハンドオーバすることができる後にのみ完了しました。
機能生産ラインに挿入されたプラグのように、特定の時間に生産ライン上の処理リソースを行います。

WebpackTapableこの複雑な生産ラインを整理します。
Webpackそれだけで興味のあるイベントに耳を傾ける必要が差し込み、動作中のイベントをブロードキャストします、生産ラインの動作を変更するには、この生産ラインに追加することができます。
Webpackイベントフローの仕組みがよく、システム全体のスケーラビリティを作り、秩序のプラグインを確認します。

OK、今私はあなたがしなければならないと信じているwebpackメカニズムは、予備的な理解を持って実行します。次に、我々は徐々にプラグインを書き始めました。

3.プラグインの作成

私たちはしばしば、我々が通常使用する方法として使用されることを知っている誰かのプラグイン:

module.exports = {
    entry:'',
    output:{},
    module: {},
    plugins: [
        new XXXPlugin(options)
    ]
}

我々は、頻繁に使用される上記のコードから見ることができる場合、プラグnew XXXPlugin()、プラグイン、それがクラスであるすなわち、プラグインの使用であるnewプラグ、プラグおよびクラスのコンストラクタに必要な設定パラメータの一例機能。次のようにその後、我々は、最初のステップで書かれたプラグインを書くことができます。

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

その後、読み取ることによって、webpackソースコードのwebpack.jsを:46行を私たちは知っています:

if (options.plugins && Array.isArray(options.plugins)) {
    for (const plugin of options.plugins) {
        if (typeof plugin === "function") {
            plugin.call(compiler, compiler);
        } else {
            plugin.apply(compiler);
        }
    }
}

ときにwebpack内部コールプラグインプラグインのインスタンスを呼び出す場合applyの方法、及びその着信compilerオブジェクト、OK、クラス内のプラグインを追加するために、来るようにプラグインを書き込む第2ステップapplyの方法を。

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

これはcomplier、その後、何ですか?ここでは詳しく説明する必要があります。

開発にはPlugin2つのオブジェクトが最も一般的であるとき、CompilerそしてCompilationそれらがから継承されTapableているPluginWebpackの間のブリッジ。
Compilerそして、Compilation次のような意味があります:

  • Compilerオブジェクトは完全表しwebpack環境構成を。このオブジェクトは、起動webpackを含め、すべての動作設定を確立し、設定する使い捨てであることoptionsloader及びpluginときにwebpackプラグイン環境を適用する、プラグインは、この受信するCompiler基準オブジェクト。あなたはアクセスするためにそれを使用することができwebpack、メイン環境を。
  • Compilationオブジェクトは、バージョン建設リソースを表します。実行している場合はwebpack、開発環境のミドルウェアを、ファイルの変更が検出されるたびに、それは新しい作成されますCompilationコンパイル、リソースの新しいセットを生成するために。Compilation現在のリソースモジュールの目標性能は、コンパイルされたリソースは、ファイルを変更し、依存ステータス情報を追跡しました。Compilationオブジェクトはまた、重要なタイミング補正の数を提供し、処理するためのカスタムプラグインを実行するときに使用することを選択しました。

Compilerそして、Compilationの違いは次のとおりです。Compiler全体の代表Webpack開始からは、ライフサイクルを閉じますが、するCompilationだけで、新しいコンパイルを表します。

使用Compilerあなたがアクセスできるオブジェクトをwebpackメインの環境を、あなたが聞くことができるwebpack放送のうち実行中の一連のイベント。OK、ここに私たちは徹底的に理解し、元のコア部分は、プラグインであるapply方法、リスニングすることで、この中の方法webpackとのうち、右側のタイムイベント放送で正しいことをやって。この時点で、あなたも疑問を持っていることがあります。私たちは、どのようなイベントを監視しますか?またはWebPACKのは、どのイベントプロセスを通じて放送されていますか?次に、私たちは、ソースコードを読む必要があるwebpackイベントが放送されている実行全体のアウトを参照してください。

4. WebPACKのワークフロー

Webpack ワークフローはシリアルプロセスで、次の手順では、最初から最後まで続きます。

  • 初期化パラメータ:設定ファイルからとShell連結損益計算書のパラメータを読み込み、最後のパラメータを得ます。
  • コンパイルを開始します取得するには、パラメータの初期化ステップを過ごすCompiler、すべてのプラグインの設定をロード、オブジェクトの実装オブジェクトのrunコンパイルを開始する方法を。
  • OKエントランス:構成によればentry、すべてのエントリ文書を識別するために、
  • モジュールをコンパイルします。入り口ファイルから、コンパイルするすべての設定されたローダーモジュールに呼びかけ、その後、モジュールに依存するモジュールを検索して、再帰的にすべてのファイルまでこのステップは、このステップでの処理の入り口に依存していました。
  • 完了モジュールのコンパイル:使用してステップ4の後Loader、すべての翻訳モジュールの後、最終的なコンテンツの後に得られた各モジュールが翻訳され、それらの間の依存関係。
  • 出力リソースが:入口と、モジュール間の依存関係が複数のモジュールを含む1つに組み立てられChunk、その後の各Chunk単一のファイルに切り替えるための最後の機会を出力リストに追加され、このステップは、出力内容に変更することができます。
  • 出力完了:ウェル出力の内容を決定した後、パスとファイル名の出力を決定するための構成によれば、ファイルの内容は、ファイルシステムに書き込まれます。

上記プロセスにおいて、Webpack関心のあるイベントをプラグインを聞いた後に、特定のロジックを実行し、プラグインを呼び出すことができ、特定の時点における特定のイベントが放送されるWebpack設けAPI変更Webpack操作結果。

初期化フェーズ、コンパイル段、出力段:イベントの放送による3つの段階に大別することができます。次のように3つの段階に発生したいくつかの一般的なイベント。

4.1初期化フェーズ

イベント名 説明
初期化パラメータ コンフィギュレーションファイルからパラメータを読み、シェルステートメントをマージし、最終的なパラメータを取得します。
インスタンス化コンパイラ ファイルの責任2.Compilerステップ例で得られた1支出コンパイラの初期化パラメータと3.Compilerインスタンスを聴き始める完全なコンパイルのWebPACK構成、唯一のグローバルコンパイラのインスタンスが含まれています。
読み込みプラグイン プラグは、すべてのイベントの後続ノードを監視できるように順番に1プラグは、適用方法を呼び出します。コンパイラプラグインのインスタンスを渡すと同時に、コンパイラが提供するプラグインAPIのWebPACKの呼び出しを容易にするために、引用しました。
環境 以下のための後続の検索を容易にし、文書を読むことをコンパイラオブジェクトにNode.jsの形式のファイルシステムを使うようになりました
エントリー・オプション 対応するエントリEntryPluginの各インスタンス用に構成さEntrysを読み、エントリの後仕事の準備をする再帰解像度
後のプラグイン メソッド呼び出しは、すべての設定を内蔵しており、プラグインを終え適用
アフターリゾルバ 設定の初期化リゾルバによっては、リゾルバは、ファイル・システム・ファイルに指定されたパスを見つけるための責任があります

4.2コンパイル相

イベント名 説明
ラン 新しいコンパイルを開始します
-見て実行します そして、それはリスニングモードでコンパイルを開始していることを、あなたは新しいコンパイルを再起動する大手のファイルが変更されたこのイベントに取得することができますを除いて、同様の実行
コンパイル イベントが開始するために、新しいプラグインにコンパイラに伝えることですが、コンパイラプラグインのオブジェクトを渡します。
編集 WebPACKのが開発モードで実行している場合場合は、すべての時間は、ファイルの変更を検出し、新しいコンパイルが作成されます。コンパイルオブジェクトは、現在のモジュールのリソース、コンパイルされたリソース、ファイルの変更が含まれています。コンパイルオブジェクトは、プラグインのコールバックが拡大しない多くのイベントを提供します。
作ります コンパイルは、新たなマスターがコンパイルし始め作成されています
アフターコンパイル 実装は、コンパイルが完了したら
無効 存在しないファイルに直面したときに、ファイルのコンパイルエラーは、この異常なイベントをトリガすると、イベントはWebPACKの出口が発生することはありません

4.3出力段

イベント名 説明
シール 変換によってすべてのモジュールとその依存関係ローダーモジュールが完了すると、依存関係のチャンクの生成を開始
Addcudak リソースの生成
createChunkAssets リソースの作成
getRenderManifest レンダリングされるファイルの説明
与えます ソースの描画
afterCompile エンドをコンパイルします
shouldEmit 必要な出力は良い生成されたすべてのファイルは、出力を接続する必要があり、そうでないどのファイル尋ねます。
発します 良い出力するファイルを決定した後、あなたがアクセスして出力を変更することができ、ファイル出力を行います。
emitRecords 書かれたレコード
完了 フル・コンパイルと出力のプロセスが正常に完了
failed 如果在编译和输出的流程中遇到异常,导致Webpack 退出, 就会直接跳转到本步骤,插件可以在本事件中获取具体的错误原因

更多的事件钩子请求查看这里:Compiler对象事件Compilation对象事件

5. 继续编写插件

知道了广播了哪些事件之后,我们就可以在apply中监听事件并编写相应的逻辑啦。如下:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        // 监听某个事件
        compiler.hooks.'compiler事件名称'.tap('XXXPlugin', (compilation)=> {
            //编写相应逻辑
        });
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

当我们监听Compiler对象事件中的compilation事件时,此时可以在回调函数中再次监听compilation对象里的事件,如下:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        // 监听某个事件
        compiler.hooks.compilation.tap('XXXPlugin', (compilation)=> {
            compilation.hooks.'compilation事件名称'.tap('optimize', () => {
                //编写相应逻辑
                
            });
        });
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

5.1 异步插件

有一些事件使用了异步钩子AsyncHook,此时在监听这些事件的时候我们就需要使用tapAsynctapPromise,并且还需要额外传入一个 callback 回调函数,在插件运行结束时,必须调用这个回调函数,如下:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        // 监听某个事件
        compiler.hooks.emit.tapAsync('XXXPlugin', (compilation,callback)=> {
            setTimeout(function () {
                console.log('异步任务完成');
                callback();
            },500);
        });
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

OK,以上就是编写一个webpack插件所有需要用到的东西,接下里我们就来编写一个简单的插件做个示例。

6. 插件示例

在这里我们编写一个简单的插件,该插件的功能是:将webpack输出的所有文件打包压缩成一个zip压缩包,该插件接收一个配置参数filename,表示最终生成的压缩包文件名称。其代码如下:

定义ZipPlugin插件:

const { RawSource } = require("webpack-sources");
const JSZip = require("jszip");
class ZipPlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    compiler.hooks.emit.tapAsync("ZipPlugin", (compilation, callback) => {
      var zip = new JSZip();
      for (let filename in compilation.assets) {
        const source = compilation.assets[filename].source();
        zip.file(filename, source);
      }
      zip.generateAsync({ type: "nodebuffer" }).then(content => {
        compilation.assets[this.options.filename || 'fileList.zip'] = new RawSource(content);
        callback();
      });
    });
  }
}
module.exports = ZipPlugin;

代码说明:

  1. 首先获取到用户传入的配置对象,将其存入this.options内。
  2. 我们要想在webpack输出完成后将输出的所有文件打包成zip压缩包,那么就必须监听webpack输出完成后的那个事件,即emit事件。并且该事件为异步事件,所以使用tabAsync监听。
  3. 通过webpack的输出资源对象compilation.assets获取到输出的所有文件,遍历这些文件,使用JSZip包将这些文件都放入一个zip压缩包内。
  4. 然后将这个压缩包挂载到webpack的输出资源对象compilation.assets上,使其跟随输出资源一同被输出,该压缩包的文件名使用用户传入的filename,如果用户未传入则使用默认的名字fileList.zip
  5. 最后由于是异步事件,在运行结束时,必须调用callback()

使用ZipPlugin插件:

const ZipPlugin  = require('./src/plugins/ZipPlugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve('dist'),
        filename: 'bundle.js'
    },
    plugins: [
      // 使用ZipPlugin插件
      new ZipPlugin({
          filename:'NLRX.zip'
      })
    ]
}

7. 总结

那么我们可以简单总结一下,一个插件的构成部分:

  • 一个插件就是一个类。
  • 在插件类上定义一个 apply 方法。
  • apply方法中监听一个webpack 自身的事件钩子。
  • 处理 webpack 内部实例的特定数据。
  • 功能完成后调用 webpack 提供的回调。

相信读完这篇文章,你应该就能写出一个自己的插件了。

(完)

おすすめ

転載: www.cnblogs.com/wangjiachen666/p/11586438.html