插件向第三方開發者提供了 webpack 引擎中完整的能力。使用階段式的構建回調,開發者能夠引入它們本身的行爲到 webpack 構建流程中。建立插件比建立 loader 更加高級,由於你將須要理解一些 webpack 底層的內部特性來作相應的鉤子,因此作好閱讀一些源碼的準備!javascript
webpack
插件由如下組成:java
apply
方法。// 一個 JavaScript 命名函數。 function MyExampleWebpackPlugin() { }; // 在插件函數的 prototype 上定義一個 `apply` 方法。 MyExampleWebpackPlugin.prototype.apply = function(compiler) { // 指定一個掛載到 webpack 自身的事件鉤子。 compiler.plugin('webpacksEventHook', function(compilation /* 處理 webpack 內部實例的特定數據。*/, callback) { console.log("This is an example plugin!!!"); // 功能完成後調用 webpack 提供的回調。 callback(); }); };
在插件開發中最重要的兩個資源就是 compiler
和 compilation
對象。理解它們的角色是擴展 webpack 引擎重要的第一步。webpack
compiler
對象表明了完整的 webpack 環境配置。這個對象在啓動 webpack 時被一次性創建,並配置好全部可操做的設置,包括 options,loader 和 plugin。當在 webpack 環境中應用一個插件時,插件將收到此 compiler 對象的引用。可使用它來訪問 webpack 的主環境。git
compilation
對象表明了一次資源版本構建。當運行 webpack 開發環境中間件時,每當檢測到一個文件變化,就會建立一個新的 compilation,從而生成一組新的編譯資源。一個 compilation 對象表現了當前的模塊資源、編譯生成資源、變化的文件、以及被跟蹤依賴的狀態信息。compilation 對象也提供了不少關鍵時機的回調,以供插件作自定義處理時選擇使用。github
這兩個組件是任何 webpack 插件不可或缺的部分(特別是 compilation
),所以,開發者在閱讀源碼,並熟悉它們以後,會感到獲益匪淺:web
插件是由「具備 apply
方法的 prototype 對象」所實例化出來的。這個 apply
方法在安裝插件時,會被 webpack compiler 調用一次。apply
方法能夠接收一個 webpack compiler 對象的引用,從而能夠在回調函數中訪問到 compiler 對象。一個簡單的插件結構以下:api
function HelloWorldPlugin(options) { // 使用 options 設置插件實例…… } HelloWorldPlugin.prototype.apply = function(compiler) { compiler.plugin('done', function() { console.log('Hello World!'); }); }; module.exports = HelloWorldPlugin;
而後,要安裝這個插件,只須要在你的 webpack 配置的 plugin
數組中添加一個實例:數組
var HelloWorldPlugin = require('hello-world'); var webpackConfig = { // ... 這裏是其餘配置 ... plugins: [ new HelloWorldPlugin({options: true}) ] };
使用 compiler 對象時,你能夠綁定提供了編譯 compilation 引用的回調函數,而後拿到每次新的 compilation 對象。這些 compilation 對象提供了一些鉤子函數,來鉤入到構建流程的不少步驟中。架構
function HelloCompilationPlugin(options) {} HelloCompilationPlugin.prototype.apply = function(compiler) { // 設置回調來訪問 compilation 對象: compiler.plugin("compilation", function(compilation) { // 如今,設置回調來訪問 compilation 中的步驟: compilation.plugin("optimize", function() { console.log("Assets are being optimized."); }); }); }; module.exports = HelloCompilationPlugin;
關於 compiler
, compilation
的可用回調,和其它重要的對象的更多信息,請查看 插件 文檔。app
有一些編譯插件中的步驟是異步的,這樣就須要額外傳入一個 callback 回調函數,而且在插件運行結束時,_必須_調用這個回調函數。
function HelloAsyncPlugin(options) {} HelloAsyncPlugin.prototype.apply = function(compiler) { compiler.plugin("emit", function(compilation, callback) { // 作一些異步處理…… setTimeout(function() { console.log("Done with async work..."); callback(); }, 1000); }); }; module.exports = HelloAsyncPlugin;
一旦能咱們深刻理解 webpack compiler 和每一個獨立的 compilation,咱們依賴 webpack 引擎將有無限多的事能夠作。咱們能夠從新格式化已有的文件,建立衍生的文件,或者製做全新的生成文件。
讓咱們來寫一個簡單的示例插件,生成一個叫作 filelist.md
的新文件;文件內容是全部構建生成的文件的列表。這個插件大概像下面這樣:
function FileListPlugin(options) {} FileListPlugin.prototype.apply = function(compiler) { compiler.plugin('emit', function(compilation, callback) { // 在生成文件中,建立一個頭部字符串: var filelist = 'In this build:\n\n'; // 遍歷全部編譯過的資源文件, // 對於每一個文件名稱,都添加一行內容。 for (var filename in compilation.assets) { filelist += ('- '+ filename +'\n'); } // 將這個列表做爲一個新的文件資源,插入到 webpack 構建中: compilation.assets['filelist.md'] = { source: function() { return filelist; }, size: function() { return filelist.length; } }; callback(); }); }; module.exports = FileListPlugin;
webpack 插件能夠按照它所註冊的事件分紅不一樣的類型。每個事件鉤子決定了它該如何應用插件的註冊。
applyPlugins(name: string, args: any...)
applyPluginsBailResult(name: string, args: any...)
這意味着每一個插件回調,都會被特定的 args
一個接一個地調用。 這是插件的最基本形式。許多有用的事件(例如 "compile"
, "this-compilation"
),預期插件會同步執行。
applyPluginsWaterfall(name: string, init: any, args: any...)
這種類型,每一個插件都在其餘插件依次調用以後調用,前一個插件調用的返回值,做爲參數傳入後一個插件。這類插件必須考慮其執行順序。 必須等前一個插件執行後,才能接收參數。第一個插件的值是初始值(init)
。這個模式用在與 webpack
模板相關的 Tapable 實例中(例如 ModuleTemplate
, ChunkTemplate
等)。
applyPluginsAsync(name: string, args: any..., callback: (err?: Error) -> void)
這種類型,插件處理函數在調用時,會傳入全部的參數和一個簽名爲 (err?: Error) -> void
的回調函數。處理函數按註冊時的順序調用。在調用完全部處理程序後,纔會調用 callback
。 這也是 "emit"
, "run"
等事件的經常使用模式。
applyPluginsAsyncWaterfall(name: string, init: any, callback: (err: Error, result: any) -> void)
這種類型,插件處理函數在調用時,會傳入當前值(current value)和一個帶有簽名爲 (err: Error, nextValue: any) -> void.
的回調函數。當調用的 nextValue
是下一個處理函數的當前值(current value)時,第一個處理程序的當前值是 init
。在調用完全部處理函數以後,纔會調用 callback,並將最後一個值傳入。若是其中任何一個處理函數傳入一個 err
值,則會調用此 callback 並將此 error 對象傳入,而且再也不調用其餘處理函數。 這種插件模式適用於像 "before-resolve"
和 "after-resolve"
這樣的事件。
applyPluginsAsyncSeries(name: string, args: any..., callback: (err: Error, result: any) -> void)
-並行(parallel) -
applyPluginsParallel(name: string, args: any..., callback: (err?: Error) -> void)
applyPluginsParallelBailResult(name: string, args: any..., callback: (err: Error, result: any) -> void)