Webpack的整個流程是經過tapable的事件流機制串聯起來。理解tapable對於閱讀源代碼的做用極大。本文分析的Tapable源碼的版本是0.2.7。webpack
tapable中大量的API主要是爲了實現兩個功能,一個做用是經過apply方法在complier中註冊插件;另外一個做用就是實現事件機制。web
apply方法的源碼:app
源碼意思很明顯,就是調用傳入對象的apply方法。在webpack中,這個傳入的對象就是插件的實例對象。異步
一個插件的簡單實現大體以下所示:函數
實際的例子以下:spa
var compiler = new Complier();插件
compiler.apply(new SimplePlugin());3d
tapable中其餘的API都是爲實現事件機制準備。webpack中的事件機制正是經過tapable實現的。須要進行事件處理的類經過繼承tapable類擁有事件處理的能力。下面咱們先看看tapable中是如何實現事件機制的。對象
所謂的事件處理,就是觸發事件和監聽事件。blog
觸發事件的方法:applyPlugins、applyPlugins0、applyPlugins一、applyPlugins2;applyPluginsWaterfall、applyPluginsWaterfall0、applyPluginsWaterfall一、applyPluginsWaterfall2;applyPluginsBailResult、applyPluginsBailResult一、applyPluginsBailResult二、applyPluginsBailResult三、applyPluginsBailResult四、applyPluginsBailResult5;applyPluginsAsyncSeries、applyPluginsAsync、applyPluginsAsyncSeries1;applyPluginsAsyncSeriesBailResult、applyPluginsAsyncSeriesBailResult1;applyPluginsAsyncWaterfall;applyPluginsParallel;applyPluginsParallelBailResult、applyPluginsParallelBailResult1。
監聽事件的方法:plugin
webpack中實現事件機制大概分爲三步:
第一步、須要實例化Tapable類或者實例化繼承自tapable的類
var tapable = new Tapable();
第二步、監聽特定的事件(至關於JS中的addEventListerner)
tapable.plugin("test", function(a,b,callback){
console.log('函數1:'+a+b);
callback();
});
第三步、觸發特定的事件(至關於trigger方法)
tapable.applyPluginsAsync("test", '123','qeeqeq', function(){
console.log('cb4');
});
tapable中監聽事件的方法就只有一個plugin方法,觸發事件的方法有applyPlugins、applyPluginsWaterfall等等,下面詳細介紹一下這些觸發事件的方法。
applyPlugins方法使用說明:
用法:
var tapable = new Tapable();
tapable.applyPlugins('eventname', param1, param2, ...... paramn),
tapable.plugin('eventname', function(param1, param2,...... paramn){})
applyPlugins0使用說明:
源碼:
用法:
var tapable = new Tapable();
tapable.applyPlugins0('eventname');
tapable.plugin('eventname', funtion(){});
applyPlugins1的使用說明:
源碼:
用法:
var tapable = new Tapable();
tapable.applyPlugins0('eventname', param1);
tapable.plugin('eventname', funtion(param1){});
applyPlugins2的使用說明:
源碼:
用法:
var tapable = new Tapable();
tapable.applyPlugins0('eventname', param1, param2);
tapable.plugin('eventname', funtion(param1, param2){});
小結:applyPlugins0、applyPlugins一、applyPlugins2用法與applyPlugins相似,只是監聽函數對應的參數不一樣而已。applyPlugins0對應的監聽函數沒有參數;applyPlugins1對應的監聽函數有一個參數;applyPlugins2對應的監聽函數有兩個參數。
這組方法的意思是觸發某個特定的事件,這個特定的事件會被一系列的流程監聽。每一個流程處理完後,將結果做爲參數傳遞給下一個監聽函數繼續處理,直至全部的監聽函數執行完畢。最終觸發事件的函數獲取全部監聽函數處理的結果。
applyPluginsWaterfall使用說明:
源碼:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue, param1, param2,......, paramn){});
tapable.plugin('eventname', function(processValue, param1, param2,......, paramn){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue, param1, param2,......paramn);
applyPluginsWaterfall0使用說明:
源碼:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue){});
tapable.plugin('eventname', function(processValue){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue);
applyPluginsWaterfall1使用說明:
源碼:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue, param1){});
tapable.plugin('eventname', function(processValue, param1){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue, param1);
applyPluginsWaterfall2使用說明:
源碼:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue, param1, param2){});
tapable.plugin('eventname', function(processValue, param1, param2){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue, param1, param2);
小結:applyPluginsWaterfall系列觸發的事件在參數中須要傳入默認值(須要被加工的數據),在監聽函數處理完後,須要獲取默認值被處理完後的結果。
這個系列的觸發函數須要有返回值,它還有一個特殊之處在監聽函數有返回值的狀況下,會提早返回。僅以applyPluginsBailResult爲例,其餘幾個方法只是參數不一樣而已。
源碼:
用法:
小結:從執行結果能夠看出第二個監聽函數返回告終果後,第三次監聽就沒有執行。最終的返回結果是第二次監聽的結果。
僅以applyPluginsAsync爲例進行分析
源碼:
用法:
小結:從源碼和執行結果上看,這組方法的做用是模仿異步的工做流程,在一個監聽執行完後,在這個監聽的回調函數裏面去執行下一個監聽函數,相似異步流程。
以applyPluginsAsyncSeriesBailResult爲例進行分析
源碼:
用法:
從源碼和執行結果上分析,applyPluginsAsyncSeriesBailResult函數時以異步的方式進行調用監聽函數,但能夠提早返回(監聽函數的回調函數中有參數)。
源碼:
用法:
小結:applyPluginsAsyncWaterfall方法是以異步的方式串聯起監聽函數,每一個監聽函數將前一個函數的處理結果做爲參數繼續處理,監聽函數的整個執行過程就像流水線同樣運做。
源碼:
用法:
小結:從源碼和運行結果來看,applyPluginsParallel方法觸發的方法是並行執行的,三個監聽函數最後調用的callback函數(這個callback指的是觸發函數執行的回調)與代碼的編寫順序無關,與實際結束的時間相關。三個監聽函數,最後執行的纔會調用觸發函數中的回調函數。
源碼:
用法:
小結:從源碼和執行結果分析,applyPluginsParallelBailResult觸發的方法是並行執行,與applyPluginsParallel不一樣的是,觸發函數的回調能夠提早執行(沒必要等全部的監聽函數執行完畢)。
apply方法用來註冊插件。
plugin用來監聽具體事件。
applyPlugins、applyPlugins0、applyPlugins一、applyPlugins2;applyPluginsWaterfall、applyPluginsWaterfall0、applyPluginsWaterfall一、applyPluginsWaterfall2;applyPluginsBailResult、applyPluginsBailResult一、applyPluginsBailResult二、applyPluginsBailResult三、applyPluginsBailResult四、applyPluginsBailResult5;applyPluginsAsyncSeries、applyPluginsAsync、applyPluginsAsyncSeries1;applyPluginsAsyncSeriesBailResult、applyPluginsAsyncSeriesBailResult1;applyPluginsAsyncWaterfall;applyPluginsParallel;applyPluginsParallelBailResult、applyPluginsParallelBailResult1 這些方法用來觸發具體事件。
觸發函數的區別以下表所示:
方法名 | 監聽函數的回調函數的最後一個參數是不是函數 | 監聽函數執行方式 | 觸發函數是否有返回值 | 觸發函數的參數中是否有回調函數 | 觸發函數的回調函數可否提早執行 |
applyPlugins | 否 | 不涉及 | 無 | 否 | 不涉及 |
applyPluginsWaterfall | 否 | 不涉及 | 有 | 否 | 不涉及 |
applyPluginsBailResult | 否 | 不涉及 | 有 | 否 | 不涉及 |
applyPluginsAsyncSeries | 是 | 異步 | 無 | 有 | 監聽函數正常執行不會提早執行 |
applyPluginsAsyncSeriesBailResult | 是 | 異步 | 無 | 有 | 能夠 |
applyPluginsAsyncWaterfall | 是 | 異步 | 無 | 有 | 監聽函數正常執行不會提早執行 |
applyPluginsParallel | 是 | 同步 | 無 | 有 | 監聽函數正常執行不會提早執行 |
applyPluginsParallelBailResult | 是 | 同步 | 無 | 有 | 能夠 |
說明:
監聽函數指的是:plugin函數
監聽函數的回調函數指的是(標紅的函數):plugin('eventname', callbackfn())
觸發函數指的是:applyPlugins等函數
觸發函數的回調函數指的是(標紅的函數):tapable.applyPluginsAsync('eventname', params, function(){})