插件
webpack4.0之前的插件是一个具有apply方法的对象,webpack在初始化的时候,会调用所有插件的apply方法。插件的apply方法里一般会通过webpack暴露的compileer和complication对象的plugin方法来进行事件的注册,compileer和complication对象都继承于Tapable。关于webpack4.0之前的插件的开发demo请点这里
Tapable
tapable是webpack的事件流核心库,类似于nodejs的EventEmitter,其中webpack4.0之前的版本都是使用Tapable@0版本,从webpack4.0开始使用Tapable@1版本,本篇文章针对的是Tapable@0,所使用的具体版本为Tapable@0.2.8。Tapable使用plugin(类似EventEmitter.on)方法来注册事件,使用applyPlugin*(类似EventEmitter.emit)来触发事件。
获取Tapable
下载后修改44行代码使其挂载到window,方便测试
// module.exports = Tapable;
window.Tapable = Tapable;
示例
<!DOCTYPE html>
<html>
<head>
<title>tapable-demo</title>
<script type="text/javascript" src="./Tapable.js"></script>
</head>
<body>
<script type="text/javascript">
var tap = new Tapable();
//applyPlugins:依次执行所有插件
tap.plugin('test',function(a,b){
console.log('test1',a,b);
})
tap.plugin('test',function(a,b){
console.log('test2',a,b);
})
tap.applyPlugins('test','arg1','arg2');
console.log('-----------------------');
//applyPluginsWaterfall:依次执行插件,前一个插件的返回值将作为下一个插件的参数
tap.plugin('testWaterfall',function(a,b){
console.log('testWaterfall1',a,b);
//返回值作为下一个插件的第一个返回值
return 1;
})
tap.plugin('testWaterfall',function(a,b){
console.log('testWaterfall2',a,b);
})
tap.applyPluginsWaterfall('testWaterfall','arg1','arg2');
console.log('-----------------------');
//applyPluginsBailResult(依次执行插件,当插件返回值不为undefined时,停止执行之后的插件)
tap.plugin('testBailResult',function(a,b){
console.log('testBailResult1',a,b);
//返回值不为undefined,其后的插件将不再执行
return null;
})
tap.plugin('testBailResult',function(a,b){
console.log('testBailResult2',a,b);
})
tap.applyPluginsBailResult('testBailResult','arg1','arg2');
console.log('-----------------------');
//applyPluginsAsyncSeries:异步的执行插件,后一个插件只有在前一个插件执行回调后才会开始执行,如果有一个插件回调的参数不为false,则将停止执行其后的插件
tap.plugin('testAsyncSeries',function(a,b,cb){
console.log('testAsyncSeries1',a,b);
//执行了回调后才会接着执行接下来的插件
cb();
})
tap.plugin('testAsyncSeries',function(a,b,cb){
console.log('testAsyncSeries2',a,b);
//回调参数不为false,将不再继续执行
cb(1);
})
tap.plugin('testAsyncSeries',function(a,b,cb){
console.log('testAsyncSeries3',a,b);
})
tap.applyPluginsAsyncSeries('testAsyncSeries','arg1','arg2',function(...arg){
console.log('testAsyncSeriesCb',...arg);
})
console.log('-----------------------');
//applyPluginsAsyncSeriesBailResult:和applyPluginsAsyncSeries类似,只是回调停止继续执行的判断条件不同
tap.plugin('testAsyncSeriesBailResult',function(a,b,cb){
console.log('testAsyncSeriesBailResult1',a,b);
//执行了回调后才会接着执行接下来的插件
cb();
})
tap.plugin('testAsyncSeriesBailResult',function(a,b,cb){
console.log('testAsyncSeriesBailResult2',a,b);
//回调参数个数大于0,将不再继续执行
cb(null);
})
tap.plugin('testAsyncSeriesBailResult',function(a,b,cb){
console.log('testAsyncSeriesBailResult3',a,b);
})
tap.applyPluginsAsyncSeriesBailResult('testAsyncSeriesBailResult','arg1','arg2',function(...arg){
console.log('testAsyncSeriesBailResultCb',...arg);
})
console.log('-----------------------');
//applyPluginsAsyncWaterfall:和applyPluginsAsyncSeries类似,只不过插件回调参数只能是两个
tap.plugin('testAsyncWaterfall',function(a,cb){
console.log('testAsyncWaterfall1',a);
//执行了回调后才会接着执行接下来的插件,第一个必须为false,第二值用来传递给下个插件
cb(null,'value');
})
tap.plugin('testAsyncWaterfall',function(a,cb){
console.log('testAsyncWaterfall2',a);
//回调参数不为false,将不再继续执行
cb(1);
})
tap.plugin('testAsyncWaterfall',function(a,cb){
console.log('testAsyncWaterfall3',a);
})
tap.applyPluginsAsyncWaterfall('testAsyncWaterfall','arg1',function(...arg){
console.log('testAsyncWaterfallCb',...arg);
})
console.log('-----------------------');
//applyPluginsParallel:通过for循环并行的执行所有插件,如果某个插件执行了回调且回调参数不为false,则其后所有未执行的插件将不再执行。可能存在插件异步调用了回调,这时候后面的插件可能已经执行了
tap.plugin('testParallel',function(a,b,cb){
console.log('testParallel1',a,b);
})
tap.plugin('testParallel',function(a,b,cb){
console.log('testParallel2',a,b);
//回调参数不为false,将不再继续执行
cb(1);
//如果换成异步,则后面的插件依然能执行
// setTimeout(function(){
// cb(1);
// },0)
})
tap.plugin('testParallel',function(a,b,cb){
console.log('testParallel3',a,b);
})
tap.applyPluginsParallel('testParallel','arg1','arg2',function(...arg){
console.log('testParallelCb',...arg);
})
console.log('-----------------------');
//applyPluginsParallelBailResult:通过for循环执行所有插件,只有第一个插件的回调能执行
tap.plugin('testParallelBailResult',function(a,b,cb){
console.log('testParallelBailResult1',a,b);
//回调函数只有第一次执行,并且参数个数要大于0
setTimeout(function(){
cb(null,'value');
},100)
})
tap.plugin('testParallelBailResult',function(a,b,cb){
console.log('testParallelBailResult2',a,b);
cb(1);
})
tap.plugin('testParallelBailResult',function(a,b,cb){
console.log('testParallelBailResult3',a,b);
})
tap.applyPluginsParallelBailResult('testParallelBailResult','arg1','arg2',function(...arg){
console.log('testParallelBailResultCb',...arg);
})
</script>
</body>
</html>
运行结果: