插件向第三方开发者提供了 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;