插件向第三方開發者提供了 webpack 引擎中完整的能力。使用階段式的構建回調,開發者能夠引入它們本身的行爲到 webpack 構建流程中。建立插件比建立 loader 更加高級,由於你將須要理解一些 webpack 底層的內部特性來實現相應的鉤子。webpack
一、一個具名 JavaScript 函數web
二、在它的原型上定義 apply
方法。api
三、指定一個觸及到 webpack 自己的 事件鉤子。數組
四、操做 webpack 內部的實例特定數據。promise
五、在實現功能後調用 webpack 提供的 callback。 架構
// 一個 JavaScript class class MyExampleWebpackPlugin { // 將 `apply` 定義爲其原型方法,此方法以 compiler 做爲參數 apply(compiler) { // 指定要附加到的事件鉤子函數 compiler.hooks.emit.tapAsync( 'MyExampleWebpackPlugin', (compilation, callback) => { console.log('This is an example plugin!'); console.log('Here’s the `compilation` object which represents a single build of assets:', compilation); // 使用 webpack 提供的 plugin API 操做構建結果 compilation.addModule(/* ... */); callback(); } ); } }
插件是由一個構造函數(此構造函數上的 prototype 對象具備 apply
方法)的所實例化出來的。這個 apply
方法在安裝插件時,會被 webpack compiler 調用一次。apply
方法能夠接收一個 webpack compiler 對象的引用,從而能夠在回調函數中訪問到 compiler 對象。app
class HelloWorldPlugin { apply(compiler) { compiler.hooks.done.tap('Hello World Plugin', ( stats /* 在 hook 被觸及時,會將 stats 做爲參數傳入。 */ ) => { console.log('Hello World!'); }); } } module.exports = HelloWorldPlugin;
而後,要使用這個插件,在你的 webpack 配置的 plugins
數組中添加一個實例:異步
// webpack.config.js var HelloWorldPlugin = require('hello-world'); module.exports = { // ... 這裏是其餘配置 ... plugins: [new HelloWorldPlugin({ options: true })] };
在插件開發中最重要的兩個資源就是 compiler
和 compilation
對象。理解它們的角色是擴展 webpack 引擎重要的第一步。async
class HelloCompilationPlugin { apply(compiler) { // tap(觸及) 到 compilation hook,而在 callback 回調時,會將 compilation 對象做爲參數, compiler.hooks.compilation.tap('HelloCompilationPlugin', compilation => { // 如今,經過 compilation 對象,咱們能夠 tap(觸及) 到各類可用的 hooks 了 compilation.hooks.optimize.tap('HelloCompilationPlugin', () => { console.log('正在優化資源。'); }); }); } } module.exports = HelloCompilationPlugin;
這裏列出 compiler
, compilation
和其餘重要對象上可用 hooks,請查看 插件 API 文檔。函數
有些插件 hooks 是異步的。想要 tap(觸及) 某些 hooks,咱們可使用同步方式運行的 tap
方法,或者使用異步方式運行的 tapAsync
方法或 tapPromise
方法。
在咱們使用 tapAsync
方法 tap 插件時,咱們須要調用 callback,此 callback 將做爲最後一個參數傳入函數。
class HelloAsyncPlugin { apply(compiler) { compiler.hooks.emit.tapAsync('HelloAsyncPlugin', (compilation, callback) => { // 作一些異步的事情…… setTimeout(function() { console.log('Done with async work...'); callback(); }, 1000); }); } } module.exports = HelloAsyncPlugin;
在咱們使用 tapPromise
方法 tap 插件時,咱們須要返回一個 promise,此 promise 將在咱們的異步任務完成時 resolve。
class HelloAsyncPlugin { apply(compiler) { compiler.hooks.emit.tapPromise('HelloAsyncPlugin', compilation => { // 返回一個 Promise,在咱們的異步任務完成時 resolve…… return new Promise((resolve, reject) => { setTimeout(function() { console.log('異步工做完成……'); resolve(); }, 1000); }); }); } } module.exports = HelloAsyncPlugin;
一旦能咱們深刻理解 webpack compiler 和每一個獨立的 compilation,咱們就能經過 webpack 引擎自己作到無窮無盡的事情。咱們能夠從新格式化已有的文件,建立衍生的文件,或者製做全新的生成文件。
咱們來寫一個簡單的示例插件,生成一個叫作 filelist.md
的新文件;文件內容是全部構建生成的文件的列表。這個插件大概像下面這樣:
class FileListPlugin { apply(compiler) { // emit 是異步 hook,使用 tapAsync 觸及它,還可使用 tapPromise/tap(同步) compiler.hooks.emit.tapAsync('FileListPlugin', (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;