webpack的工作机制是基于事件流,将各个插件串联起来,而核心就是tapable
对象
核心概念:
-
tapable 对象,是
webpack
为plugin
创建hook
的包,中文文档 -
开发webpack插件最重要两个资源是对象:compiler和compilation,这两货都是扩展了
tapable
对象- Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;
- ompilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。
Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。
webpack
插件由以下组成:
- 一个 JavaScript 命名函数。
- 在插件函数的 prototype 上定义一个
apply
方法。 - 指定一个绑定到 webpack 自身的事件钩子。
- 处理 webpack 内部实例的特定数据。
- 功能完成后调用 webpack 提供的回调。
下面是一个简单的插件结构:
// 定义一个class
class MyExampleWebpackPlugin {
// 定义 apply方法,参数是 compiler
apply(compiler) {
// 附加一个指定的钩子
compiler.hooks.emit.tap(
'MyExampleWebpackPlugin',
(compilation) => {
// Manipulate the build using the plugin API provided by webpack
compilation.addModule(/* ... */);
compilation.hooks.buildModule.tap('MyExampleWebpackPlugin', () => {
//do something...
});
}
);
}
}
在同步钩子中, tap
是唯一的绑定方法,异步钩子通常支持异步插件
// promise: 绑定promise钩子的API
myCar.hooks.calculateRoutes.tapPromise("GoogleMapsPlugin", (source, target, routesList) => {
// return a promise
return google.maps.findRoute(source, target).then(route => {
routesList.add(route);
});
});
// tapAsync:绑定异步钩子的API
compilation.hooks.additionalAssets.tapAsync('MyPlugin', callback => {
download('https://img.shields.io/npm/v/webpack.svg', function(resp) {
if(resp.status === 200) {
compilation.assets['webpack-version.svg'] = toAsset(resp);
callback();
} else {
callback(new Error('[webpack-example-plugin] Unable to download the image'));
}
});
});
tapable
的钩子类型:
每一个钩子都可以tap 一个或者多个函数, 他们如何运行,取决于他们的钩子类型
- 基本的钩子, (钩子类名没有waterfall, Bail, 或者 Loop 的 ), 这个钩子只会简单的调用每个tap进去的函数
-
Waterfall
, 一个waterfall 钩子,也会调用每个tap进去的函数,不同的是,他会从每一个函数传一个返回的值到
下一个函数 -
Bail
, Bail 钩子允许更早的退出,当任何一个tap进去的函数,返回任何值, bail类会停止执行其他的函数执行.(类似 Promise.race()) -
Loop
, TODO(我.... 这里也没描述,应该是写文档得时候 还没想好这个要怎么写,我尝试看他代码去补全,不
过可能需要点时间.)
此外,钩子可以是同步的,也可以是异步的,Sync, AsyncSeries 和 AsyncParallel ,从名字就可以看出,哪些是可以绑定异步函数的
-
Sync
, 一个同步钩子只能tap同步函数, 不然会报错. -
AsyncSeries
, 一个 async-series 钩子 可以tap 同步钩子, 基于回调的钩子(我估计是类似chunk的东西)和一个基于promise的钩子(使用myHook.tap(), myHook.tapAsync() 和 myHook.tapPromise().).他会按顺序的调用每个方法. -
AsyncParallel
, 一个 async-parallel 钩子跟上面的 async-series 一样 不同的是他会把异步钩子并行执行(并行执行就是把异步钩子全部一起开启,不按顺序执行).
实战,编写一个webpack插件
class FileListPlugin{
apply (compiler) {
compiler.hooks.emit.tap('FileListPlugin', function(compilation) {
// 在生成文件中,创建一个头部字符串:
var filelist = 'In this build:\n\n';
// 遍历所有编译过的资源文件,
// 对于每个文件名称,都添加一行内容。
for (var filename in compilation.assets) {
filelist += ('- '+ filename +'\n');
}
// 将这个列表作为一个新的文件资源,插入到 webpack 构建中:
compilation.assets['filelist.md'] = {
// 返回文件内容
source: function() {
// 既可以是代表文本文件的字符串,也可以是代表二进制文件的 Buffer
return filelist;
},
// 返回文件大小
size: function() {
return filelist.length;
}
};
});
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。