如何開發webpack plugin

繼上回介紹了如何開發webpack loader 以後。趁熱打鐵,來繼續看下webpack另外一個核心組成:plugin。
下面也和loader同樣,讓咱們一塊兒從基本的官方文檔着手看起。 webpack

loader和plugin的差異

  • loader : 顧名思義,某種類型資源文件的加載器,做用於某種類型的文件上。webpack自己也是不能直接打包這些非js文件的,須要一個轉化器即loader。 loader自己是單一,簡單的,不能將多個功能放在一個loader裏。
  • plugin比loaders更加先進一點,你能夠擴展webpack的功能來知足本身的須要,換句話說,loader不能知足的時候,就須要plugin了。

如何開發一個plugin

插件將webpack引擎全部的能力暴露給第三方開發者。經過階梯式的build回調,開發者能夠在webpack編譯過程當中加入本身的行爲。開發插件比loaders更加先進一點,由於你須要理解webpack一些底層構成來添加鉤子回調。準備好讀一些源碼吧。 git

開發一個插件

一個webpack的插件由如下幾方面組成: github

  • 一個非匿名的js函數
  • 在它的原型對象上定義apply方法
  • 指明掛載自身的webpack鉤子事件
  • 操做webpack內部狀況的特定數據
  • 方法完成時喚起webpack提供的回調
    // A named JavaScript function.
    function MyExampleWebpackPlugin() {
     //
    };
    // Defines `apply` method in it's prototype.
    MyExampleWebpackPlugin.prototype.apply = function(compiler) {
    // Specifies webpack's event hook to attach itself.
    compiler.plugin('webpacksEventHook', function(compilation /* Manipulates webpack internal instance specific data. */, callback) {
      console.log("This is an example plugin!!!");
      // Invokes webpack provided callback after functionality is complete.
      callback();
    });
    };複製代碼

    編譯器和編譯

開發插件過程當中最重要的兩個對象就是compiler 和compilation。理解他們的職責是擴展webpack功能最重要的第一步 web

編譯器對象就是webpack完整的配置環境。該對象一經webpack開始執行就建立,而且經過全部可操做的設置項來設置,例如options,loaders,和plugins。當在webpack環境中應用一個插件時,該插件將會接受到一個指向該編譯器的引用。使用該編譯器來訪問主要的webpack環境。api

compilation對象是一個單獨的關於版本資源的建立。當執行webpack 開發中間件時,當一個文件的更改被檢測到就會建立一個新的compilation對象,所以產生了一些可被編譯的資源。一個compilation展示了一些信息關於當前模塊資源狀態、編譯資源、改變的文件、監視的依賴等信息。一樣提供了不少關鍵的回調,當插件擴展自定義行爲時 數組

這兩個組件是webpack 插件必需的組成部分(特別是compilation),因此開發者若是熟悉下面這些源文件將會獲益不小。 app

插件的基本結構

插件是在原型中帶有一個apply方法的實例化對象,當安裝插件的時候,這個apply方法就會被webpack調用一次。apply方法提供一個指向當前活動的webpack compiler的引用,該引用容許訪問compiler的回調。一個簡單的插件結構以下: 異步

function HelloWorldPlugin(options) {
  // Setup the plugin instance with options...
}

HelloWorldPlugin.prototype.apply = function(compiler) {
  compiler.plugin('done', function() {
    console.log('Hello World!');
  });
};

module.exports = HelloWorldPlugin;複製代碼

而後安裝一個插件,僅僅須要在你的 webpack config 中plugins對應的數組中,增長一個插件的實例便可 async

var HelloWorldPlugin = require('hello-world');

var webpackConfig = {
  // ... config settings here ...
  plugins: [
    new HelloWorldPlugin({options: true})
  ]
};複製代碼

訪問編譯

經過使用編譯器對象,你可能會綁定提供指向每一個新的compilation應用的回調。這些compilations提供了編譯過程當中不少步驟的回調函數。 ide

function HelloCompilationPlugin(options) {}

HelloCompilationPlugin.prototype.apply = function(compiler) {

  // Setup callback for accessing a compilation:
  compiler.plugin("compilation", function(compilation) {

    // Now setup callbacks for accessing compilation steps:
    compilation.plugin("optimize", function() {
      console.log("Assets are being optimized.");
    });
  });
};

module.exports = HelloCompilationPlugin;複製代碼

若是想了解更多關於在編譯器、編譯中哪些回調是可用的和其餘一些更重要的對象,輕戳plugin文檔

異步插件

一些編譯插件步驟是異步的而且提供了一個當你的插件結束編譯時必須調用的回調方法

function HelloAsyncPlugin(options) {}

HelloAsyncPlugin.prototype.apply = function(compiler) {
  compiler.plugin("emit", function(compilation, callback) {

    // Do something async...
    setTimeout(function() {
      console.log("Done with async work...");
      callback();
    }, 1000);

  });
};

module.exports = HelloAsyncPlugin;複製代碼

示例

一旦咱們打開了webpack編譯器和每一個單獨編譯的大門,咱們可使用引擎作的事情是無限可能的。咱們能夠從新格式化存在的文件、建立派生文件、徹底僞造一個新文件

讓咱們寫個簡單的示例插件,目的是生成一個新的名字爲filelist.md的文件。內容以下:列出構建過程當中全部的生成文件。這個插件大概以下:

function FileListPlugin(options) {}

FileListPlugin.prototype.apply = function(compiler) {
  compiler.plugin('emit', function(compilation, callback) {
    // Create a header string for the generated file:
    var filelist = 'In this build:\n\n';

    // Loop through all compiled assets,
    // adding a new line item for each filename.
    for (var filename in compilation.assets) {
      filelist += ('- '+ filename +'\n');
    }

    // Insert this list into the webpack build as a new file asset:
    compilation.assets['filelist.md'] = {
      source: function() {
        return filelist;
      },
      size: function() {
        return filelist.length;
      }
    };

    callback();
  });
};

module.exports = FileListPlugin;複製代碼

不一樣類型的插件

插件能夠依據其註冊的事件來分紅不一樣的類型,每一個事件鉤子決定了在觸發時如何調用該插件。

同步類型

這種類型的實例使用以下方式來調用插件

applyPlugins(name: string, args: any...)

applyPluginsBailResult(name: string, args: any...)複製代碼

這意味着每個插件的回調將伴隨特定參數args依次被調用。對插件而言這是最簡單的格式。不少有用的事件例如"compile", "this-compilation",是指望插件同步執行的。

流式類型

waterfall Plugins 經過下面的方式調用

applyPluginsWaterfall(name: string, init: any, args: any...)複製代碼

異步類型

當全部的插件被使用下面的方法異步調用的時候,即爲異步插件

applyPluginsAsync(name: string, args: any..., callback: (err?: Error) -> void)複製代碼

插件控制方法被調用,參數是全部的args和帶有這種標誌(err?: Error) -> void的回調。handler方法按照註冊回調在全部handlers被調用以後的順序來調用。對於"emit", "run"事件來講這是很經常使用的模式。

異步流

這種插件將按照流失方式來被異步使用

applyPluginsAsyncWaterfall(name: string, init: any, callback: (err: Error, result: any) -> void)複製代碼

這種插件的handler被調用時,參數是當前value和帶有這種標誌(err?: Error) -> void的回調。當被調用時,nextValue是下一個handler的當前值。第一個handler的當前值是init。全部的handler被調用以後,最後一個值將會被賦給回調。若是有的handler傳遞了一個err的值,回調將會接受err,而且不會有其餘handler被第阿勇。這種插件模式使用與於"before-resolve" and "after-resolve"之類的事件。

異步系列

這種和異步插件很類似,不一樣在於若是有點插件註冊失敗,將不會調用任何插件

applyPluginsAsyncSeries(name: string, args: any..., callback: (err: Error, result: any) -> void)複製代碼

結束語

至此,如何開發一個基本的webpack plugin 我相信你們已經知道了,若是還不太清楚的話,能夠移步w-loader查看。另外,對於我這種英語渣渣來講,翻譯起來確實難度蠻大的。此處拋磚引玉,但願你們共同探討學習。

相關文章
相關標籤/搜索