国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

淺談Webpack核心模塊tapable解析

2019-11-19 12:58:33
字體:
來源:轉載
供稿:網友

本文介紹了Webpack核心模塊tapable,分享給大家,具體如下:

前言

Webpack 是一個現代 JavaScript 應用程序的靜態(tài)模塊打包器,是對前端項目實現自動化和優(yōu)化必不可少的工具,Webpack 的 loader (加載器)和 plugin (插件)是由 Webpack 開發(fā)者和社區(qū)開發(fā)者共同貢獻的,而目前又沒有比較系統的開發(fā)文檔,想寫加載器和插件必須要懂 Webpack 的原理,即看懂 Webpack 的源碼, tapable 則是 Webpack 依賴的核心庫,可以說不懂 tapable 就看不懂 Webpack 源碼,所以本篇會對 tapable 提供的類進行解析和模擬。

tapable 介紹

Webpack 本質上是一種事件流的機制,它的工作流程就是將各個插件串聯起來,而實現這一切的核心就是 tapable ,Webpack 中最核心的,負責編譯的 Compiler 和負責創(chuàng)建 bundles 的 Compilation 都是 tapable 構造函數的實例。

打開 Webpack 4.0 的源碼中一定會看到下面這些以 Sync 、 Async 開頭,以 Hook 結尾的方法,這些都是 tapable 核心庫的類,為我們提供不同的事件流執(zhí)行機制,我們稱為 “鉤子”。

// 引入 tapable 如下const {  SyncHook,  SyncBailHook,  SyncWaterfallHook,  SyncLoopHook,  AsyncParallelHook,  AsyncParallelBailHook,  AsyncSeriesHook,  AsyncSeriesBailHook,  AsyncSeriesWaterfallHook } = require("tapable");

上面的實現事件流機制的 “鉤子” 大方向可以分為兩個類別,“同步” 和 “異步”,“異步” 又分為兩個類別,“并行” 和 “串行”,而 “同步” 的鉤子都是串行的。

Sync 類型的鉤子

1、SyncHook

SyncHook 為串行同步執(zhí)行,不關心事件處理函數的返回值,在觸發(fā)事件之后,會按照事件注冊的先后順序執(zhí)行所有的事件處理函數。

// SyncHook 鉤子的使用const { SyncHook } = require("tapable");// 創(chuàng)建實例let syncHook = new SyncHook(["name", "age"]);// 注冊事件syncHook.tap("1", (name, age) => console.log("1", name, age));syncHook.tap("2", (name, age) => console.log("2", name, age));syncHook.tap("3", (name, age) => console.log("3", name, age));// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行syncHook.call("panda", 18);// 1 panda 18// 2 panda 18// 3 panda 18

在 tapable 解構的 SyncHook 是一個類,注冊事件需先創(chuàng)建實例,創(chuàng)建實例時支持傳入一個數組,數組內存儲事件觸發(fā)時傳入的參數,實例的 tap 方法用于注冊事件,支持傳入兩個參數,第一個參數為事件名稱,在 Webpack 中一般用于存儲事件對應的插件名稱(名字隨意,只是起到注釋作用), 第二個參數為事件處理函數,函數參數為執(zhí)行 call 方法觸發(fā)事件時所傳入的參數的形參。

// 模擬 SyncHook 類class SyncHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tap(name, task) {    this.tasks.push(task);  }  call(...args) {    // 也可在參數不足時拋出異常    if (args.length < this.args.length) throw new Error("參數不足");    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 依次執(zhí)行事件處理函數    this.tasks.forEach(task => task(...args));  }}

tasks 數組用于存儲事件處理函數, call 方法調用時傳入參數超過創(chuàng)建 SyncHook 實例傳入的數組長度時,多余參數可處理為 undefined ,也可在參數不足時拋出異常,不靈活,后面的例子中就不再這樣寫了。

2、SyncBailHook

SyncBailHook 同樣為串行同步執(zhí)行,如果事件處理函數執(zhí)行時有一個返回值不為空(即返回值為 undefined ),則跳過剩下未執(zhí)行的事件處理函數(如類的名字,意義在于保險)。

// SyncBailHook 鉤子的使用const { SyncBailHook } = require("tapable");// 創(chuàng)建實例let syncBailHook = new SyncBailHook(["name", "age"]);// 注冊事件syncBailHook.tap("1", (name, age) => console.log("1", name, age));syncBailHook.tap("2", (name, age) => {  console.log("2", name, age);  return "2";});syncBailHook.tap("3", (name, age) => console.log("3", name, age));// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行syncBailHook.call("panda", 18);// 1 panda 18// 2 panda 18

通過上面的用法可以看出, SyncHook 和 SyncBailHook 在邏輯上只是 call 方法不同,導致事件的執(zhí)行機制不同,對于后面其他的 “鉤子”,也是 call 的區(qū)別,接下來實現 SyncBailHook 類。

// 模擬 SyncBailHook 類class SyncBailHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tap(name, task) {    this.tasks.push(task);  }  call(...args) {    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 依次執(zhí)行事件處理函數,如果返回值不為空,則停止向下執(zhí)行    let i = 0, ret;    do {      ret = this.tasks[i++](...args);    } while (!ret);  }}

在上面代碼的 call 方法中,我們設置返回值為 ret ,第一次執(zhí)行后沒有返回值則繼續(xù)循環(huán)執(zhí)行,如果有返回值則立即停止循環(huán),即實現 “保險” 的功能。

3、SyncWaterfallHook

SyncWaterfallHook 為串行同步執(zhí)行,上一個事件處理函數的返回值作為參數傳遞給下一個事件處理函數,依次類推,正因如此,只有第一個事件處理函數的參數可以通過 call 傳遞,而 call 的返回值為最后一個事件處理函數的返回值。

// SyncWaterfallHook 鉤子的使用const { SyncWaterfallHook } = require("tapable");// 創(chuàng)建實例let syncWaterfallHook = new SyncWaterfallHook(["name", "age"]);// 注冊事件syncWaterfallHook.tap("1", (name, age) => {  console.log("1", name, age);  return "1";});syncWaterfallHook.tap("2", data => {  console.log("2", data);  return "2";});syncWaterfallHook.tap("3", data => {  console.log("3", data);  return "3"});// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行l(wèi)et ret = syncWaterfallHook.call("panda", 18);console.log("call", ret);// 1 panda 18// 2 1// 3 2// call 3

SyncWaterfallHook 名稱中含有 “瀑布”,通過上面代碼可以看出 “瀑布” 形象生動的描繪了事件處理函數執(zhí)行的特點,與 SyncHook 和 SyncBailHook 的區(qū)別就在于事件處理函數返回結果的流動性,接下來看一下 SyncWaterfallHook 類的實現。

// 模擬 SyncWaterfallHook 類class SyncWaterfallHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tap(name, task) {    this.tasks.push(task);  }  call(...args) {    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 依次執(zhí)行事件處理函數,事件處理函數的返回值作為下一個事件處理函數的參數    let [first, ...others] = this.tasks;    return reduce((ret, task) => task(ret), first(...args));  }}

上面代碼中 call 的邏輯是將存儲事件處理函數的 tasks 拆成兩部分,分別為第一個事件處理函數,和存儲其余事件處理函數的數組,使用 reduce 進行歸并,將第一個事件處理函數執(zhí)行后的返回值作為歸并的初始值,依次調用其余事件處理函數并傳遞上一次歸并的返回值。

4、SyncLoopHook

SyncLoopHook 為串行同步執(zhí)行,事件處理函數返回 true 表示繼續(xù)循環(huán),即循環(huán)執(zhí)行當前事件處理函數,返回 undefined 表示結束循環(huán), SyncLoopHook 與 SyncBailHook 的循環(huán)不同, SyncBailHook 只決定是否繼續(xù)向下執(zhí)行后面的事件處理函數,而 SyncLoopHook 的循環(huán)是指循環(huán)執(zhí)行每一個事件處理函數,直到返回 undefined 為止,才會繼續(xù)向下執(zhí)行其他事件處理函數,執(zhí)行機制同理。

// SyncLoopHook 鉤子的使用const { SyncLoopHook } = require("tapable");// 創(chuàng)建實例let syncLoopHook = new SyncLoopHook(["name", "age"]);// 定義輔助變量let total1 = 0;let total2 = 0;// 注冊事件syncLoopHook.tap("1", (name, age) => {  console.log("1", name, age, total1);  return total1++ < 2 ? true : undefined;});syncLoopHook.tap("2", (name, age) => {  console.log("2", name, age, total2);  return total2++ < 2 ? true : undefined;});syncLoopHook.tap("3", (name, age) => console.log("3", name, age));// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行syncLoopHook.call("panda", 18);// 1 panda 18 0// 1 panda 18 1// 1 panda 18 2// 2 panda 18 0// 2 panda 18 1// 2 panda 18 2// 3 panda 18

通過上面的執(zhí)行結果可以清楚的看到 SyncLoopHook 的執(zhí)行機制,但有一點需要注意,返回值必須嚴格是 true 才會觸發(fā)循環(huán),多次執(zhí)行當前事件處理函數,必須嚴格返回 undefined ,才會結束循環(huán),去執(zhí)行后面的事件處理函數,如果事件處理函數的返回值不是 true 也不是 undefined ,則會死循環(huán)。

在了解 SyncLoopHook 的執(zhí)行機制以后,我們接下來看看 SyncLoopHook 的 call 方法是如何實現的。

// 模擬 SyncLoopHook 類class SyncLoopHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tap(name, task) {    this.tasks.push(task);  }  call(...args) {    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 依次執(zhí)行事件處理函數,如果返回值為 true,則繼續(xù)執(zhí)行當前事件處理函數    // 直到返回 undefined,則繼續(xù)向下執(zhí)行其他事件處理函數    this.tasks.forEach(task => {      let ret;      do {        ret = this.task(...args);      } while (ret === true || !(ret === undefined));    });  }}

在上面代碼中可以看到 SyncLoopHook 類 call 方法的實現更像是 SyncHook 和 SyncBailHook 的 call 方法的結合版,外層循環(huán)整個 tasks 事件處理函數隊列,內層通過返回值進行循環(huán),控制每一個事件處理函數的執(zhí)行次數。

注意:在 Sync 類型 “鉤子” 下執(zhí)行的插件都是順序執(zhí)行的,只能使用 tab 注冊。

Async 類型的鉤子

Async 類型可以使用 tap 、 tapSync 和 tapPromise 注冊不同類型的插件 “鉤子”,分別通過 call 、 callAsync 和 promise 方法調用,我們下面會針對 AsyncParallelHook 和 AsyncSeriesHook 的 async 和 promise 兩種方式分別介紹和模擬。

1、AsyncParallelHook

AsyncParallelHook 為異步并行執(zhí)行,通過 tapAsync 注冊的事件,通過 callAsync 觸發(fā),通過 tapPromise 注冊的事件,通過 promise 觸發(fā)(返回值可以調用 then 方法)。

(1) tapAsync/callAsync

callAsync 的最后一個參數為回調函數,在所有事件處理函數執(zhí)行完畢后執(zhí)行。

// AsyncParallelHook 鉤子:tapAsync/callAsync 的使用const { AsyncParallelHook } = require("tapable");// 創(chuàng)建實例let asyncParallelHook = new AsyncParallelHook(["name", "age"]);// 注冊事件console.time("time");asyncParallelHook.tapAsync("1", (name, age, done) => {  settimeout(() => {    console.log("1", name, age, new Date());    done();  }, 1000);});asyncParallelHook.tapAsync("2", (name, age, done) => {  settimeout(() => {    console.log("2", name, age, new Date());    done();  }, 2000);});asyncParallelHook.tapAsync("3", (name, age, done) => {  settimeout(() => {    console.log("3", name, age, new Date());    done();    console.timeEnd("time");  }, 3000);});// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行asyncParallelHook.callAsync("panda", 18, () => {  console.log("complete");});// 1 panda 18 2018-08-07T10:38:32.675Z// 2 panda 18 2018-08-07T10:38:33.674Z// 3 panda 18 2018-08-07T10:38:34.674Z// complete// time: 3005.060ms

異步并行是指,事件處理函數內三個定時器的異步操作最長時間為 3s ,而三個事件處理函數執(zhí)行完成總共用時接近 3s ,所以三個事件處理函數是幾乎同時執(zhí)行的,不需等待。

所有 tabAsync 注冊的事件處理函數最后一個參數都為一個回調函數 done ,每個事件處理函數在異步代碼執(zhí)行完畢后調用 done 函數,則可以保證 callAsync 會在所有異步函數都執(zhí)行完畢后執(zhí)行,接下來看一看 callAsync 是如何實現的。

// 模擬 AsyncParallelHook 類:tapAsync/callAsyncclass AsyncParallelHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tabAsync(name, task) {    this.tasks.push(task);  }  callAsync(...args) {    // 先取出最后傳入的回調函數    let finalCallback = args.pop();    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 定義一個 i 變量和 done 函數,每次執(zhí)行檢測 i 值和隊列長度,決定是否執(zhí)行 callAsync 的回調函數    let i = 0;    let done = () => {      if (++i === this.tasks.length) {        finalCallback();      }    };    // 依次執(zhí)行事件處理函數    this.tasks.forEach(task => task(...args, done));  }}

在 callAsync 中,將最后一個參數(所有事件處理函數執(zhí)行完畢后執(zhí)行的回調)取出,并定義 done 函數,通過比較 i 和存儲事件處理函數的數組 tasks 的 length 來確定回調是否執(zhí)行,循環(huán)執(zhí)行每一個事件處理函數并將 done 作為最后一個參數傳入,所以每個事件處理函數內部的異步操作完成時,執(zhí)行 done 就是為了檢測是不是該執(zhí)行 callAsync 的回調,當所有事件處理函數均執(zhí)行完畢滿足 done 函數內部 i 和 length 相等的條件時,則調用 callAsync 的回調。

(2) tapPromise/promise

要使用 tapPromise 注冊事件,對事件處理函數有一個要求,必須返回一個 Promise 實例,而 promise 方法也返回一個 Promise 實例, callAsync 的回調函數在 promise 方法中用 then 的方式代替。

// AsyncParallelHook 鉤子:tapPromise/promise 的使用const { AsyncParallelHook } = require("tapable");// 創(chuàng)建實例let asyncParallelHook = new AsyncParallelHook(["name", "age"]);// 注冊事件console.time("time");asyncParallelHook.tapPromise("1", (name, age) => {  return new Promise((resolve, reject) => {    settimeout(() => {      console.log("1", name, age, new Date());      resolve("1");    }, 1000);  });});asyncParallelHook.tapPromise("2", (name, age) => {  return new Promise((resolve, reject) => {    settimeout(() => {      console.log("2", name, age, new Date());      resolve("2");    }, 2000);  });});asyncParallelHook.tapPromise("3", (name, age) => {  return new Promise((resolve, reject) => {    settimeout(() => {      console.log("3", name, age, new Date());      resolve("3");      console.timeEnd("time");    }, 3000);  });});// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行asyncParallelHook.promise("panda", 18).then(ret => {  console.log(ret);});// 1 panda 18 2018-08-07T12:17:21.741Z// 2 panda 18 2018-08-07T12:17:22.736Z// 3 panda 18 2018-08-07T12:17:23.739Z// time: 3006.542ms// [ '1', '2', '3' ]

上面每一個 tapPromise 注冊事件的事件處理函數都返回一個 Promise 實例,并將返回值傳入 resolve 方法,調用 promise 方法觸發(fā)事件時,如果所有事件處理函數返回的 Promise 實例結果都成功,會將結果存儲在數組中,并作為參數傳遞給 promise 的 then 方法中成功的回調,如果有一個失敗就是將失敗的結果返回作為參數傳遞給失敗的回調。

// 模擬 AsyncParallelHook 類 tapPromise/promiseclass AsyncParallelHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tapPromise(name, task) {    this.tasks.push(task);  }  promise(...args) {    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 將所有事件處理函數轉換成 Promise 實例,并發(fā)執(zhí)行所有的 Promise    return Promise.all(this.tasks.map(task => task(...args)));  }}

其實根據上面對于 tapPromise 和 promise 使用的描述就可以猜到, promise 方法的邏輯是通過 Promise.all 來實現的。

2、AsyncSeriesHook

AsyncSeriesHook 為異步串行執(zhí)行,與 AsyncParallelHook 相同,通過 tapAsync 注冊的事件,通過 callAsync 觸發(fā),通過 tapPromise 注冊的事件,通過 promise 觸發(fā),可以調用 then 方法。

(1) tapAsync/callAsync

與 AsyncParallelHook 的 callAsync 方法類似, AsyncSeriesHook 的 callAsync 方法也是通過傳入回調函數的方式,在所有事件處理函數執(zhí)行完畢后執(zhí)行 callAsync 的回調函數。

// AsyncSeriesHook 鉤子:tapAsync/callAsync 的使用const { AsyncSeriesHook } = require("tapable");// 創(chuàng)建實例let asyncSeriesHook = new AsyncSeriesHook(["name", "age"]);// 注冊事件console.time("time");asyncSeriesHook.tapAsync("1", (name, age, next) => {  settimeout(() => {    console.log("1", name, age, new Date());    next();  }, 1000);});asyncSeriesHook.tapAsync("2", (name, age, next) => {  settimeout(() => {    console.log("2", name, age, new Date());    next();  }, 2000);});asyncSeriesHook.tapAsync("3", (name, age, next) => {  settimeout(() => {    console.log("3", name, age, new Date());    next();    console.timeEnd("time");  }, 3000);});// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行asyncSeriesHook.callAsync("panda", 18, () => {  console.log("complete");});// 1 panda 18 2018-08-07T14:40:52.896Z// 2 panda 18 2018-08-07T14:40:54.901Z// 3 panda 18 2018-08-07T14:40:57.901Z// complete// time: 6008.790ms

異步串行是指,事件處理函數內三個定時器的異步執(zhí)行時間分別為 1s 、 2s 和 3s ,而三個事件處理函數執(zhí)行完總共用時接近 6s ,所以三個事件處理函數執(zhí)行是需要排隊的,必須一個一個執(zhí)行,當前事件處理函數執(zhí)行完才能執(zhí)行下一個。

AsyncSeriesHook 類的 tabAsync 方法注冊的事件處理函數參數中的 next 可以與 AsyncParallelHook 類中 tabAsync 方法參數的 done 進行類比,同為回調函數,不同點在于 AsyncSeriesHook 與 AsyncParallelHook 的 callAsync 方法的 “并行” 和 “串行” 的實現方式。

// 模擬 AsyncSeriesHook 類:tapAsync/callAsyncclass AsyncSeriesHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tabAsync(name, task) {    this.tasks.push(task);  }  callAsync(...args) {    // 先取出最后傳入的回調函數    let finalCallback = args.pop();    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 定義一個 i 變量和 next 函數,每次取出一個事件處理函數執(zhí)行,并維護 i 的值    // 直到所有事件處理函數都執(zhí)行完,調用 callAsync 的回調    // 如果事件處理函數中沒有調用 next,則無法繼續(xù)    let i = 0;    let next = () => {      let task = this.tasks[i++];      task ? task(...args, next) : finalCallback();    };    next();  }}

AsyncParallelHook 是通過循環(huán)依次執(zhí)行了所有的事件處理函數, done 方法只為了檢測是否已經滿足條件執(zhí)行 callAsync 的回調,如果中間某個事件處理函數沒有調用 done ,只是不會調用 callAsync 的回調,但是所有的事件處理函數都執(zhí)行了。

而 AsyncSeriesHook 的 next 執(zhí)行機制更像 Express 和 Koa 中的中間件,在注冊事件的回調中如果不調用 next ,則在觸發(fā)事件時會在沒有調用 next 的事件處理函數的位置 “卡死”,即不會繼續(xù)執(zhí)行后面的事件處理函數,只有都調用 next 才能繼續(xù),而最后一個事件處理函數中調用 next 決定是否調用 callAsync 的回調。

(2) tapPromise/promise

與 AsyncParallelHook 類似, tapPromise 注冊事件的事件處理函數需要返回一個 Promise 實例, promise 方法最后也返回一個 Promise 實例。

// AsyncSeriesHook 鉤子:tapPromise/promise 的使用const { AsyncSeriesHook } = require("tapable");// 創(chuàng)建實例let asyncSeriesHook = new AsyncSeriesHook(["name", "age"]);// 注冊事件console.time("time");asyncSeriesHook.tapPromise("1", (name, age) => {  return new Promise((resolve, reject) => {    settimeout(() => {      console.log("1", name, age, new Date());      resolve("1");    }, 1000);  });});asyncSeriesHook.tapPromise("2", (name, age) => {  return new Promise((resolve, reject) => {    settimeout(() => {      console.log("2", name, age, new Date());      resolve("2");    }, 2000);  });});asyncParallelHook.tapPromise("3", (name, age) => {  return new Promise((resolve, reject) => {    settimeout(() => {      console.log("3", name, age, new Date());      resolve("3");      console.timeEnd("time");    }, 3000);  });});// 觸發(fā)事件,讓監(jiān)聽函數執(zhí)行asyncSeriesHook.promise("panda", 18).then(ret => {  console.log(ret);});// 1 panda 18 2018-08-07T14:45:52.896Z// 2 panda 18 2018-08-07T14:45:54.901Z// 3 panda 18 2018-08-07T14:45:57.901Z// time: 6014.291ms// [ '1', '2', '3' ]

分析上面的執(zhí)行過程,所有的事件處理函數都返回了 Promise 的實例,如果想實現 “串行”,則需要讓每一個返回的 Promise 實例都調用 then ,并在 then 中執(zhí)行下一個事件處理函數,這樣就保證了只有上一個事件處理函數執(zhí)行完后才會執(zhí)行下一個。

// 模擬 AsyncSeriesHook 類 tapPromise/promiseclass AsyncSeriesHook {  constructor(args) {    this.args = args;    this.tasks = [];  }  tapPromise(name, task) {    this.tasks.push(task);  }  promise(...args) {    // 傳入參數嚴格對應創(chuàng)建實例傳入數組中的規(guī)定的參數,執(zhí)行時多余的參數為 undefined    args = args.slice(0, this.args.length);    // 將每個事件處理函數執(zhí)行并調用返回 Promise 實例的 then 方法    // 讓下一個事件處理函數在 then 方法成功的回調中執(zhí)行    let [first, ...others] = this.tasks;    return others.reduce((promise, task) => {      return promise.then(() => task(...args));    }, first(...args));  }}

上面代碼中的 “串行” 是使用 reduce 歸并來實現的,首先將存儲所有事件處理函數的數組 tasks 解構成兩部分,第一個事件處理函數和存儲其他事件處理函數的數組 others ,對 others 進行歸并,將第一個事件處理函數執(zhí)行后返回的 Promise 實例作為歸并的初始值,這樣在歸并的過程中上一個值始終是上一個事件處理函數返回的 Promise 實例,可以直接調用 then 方法,并在 then 的回調中執(zhí)行下一個事件處理函數,直到歸并完成,將 reduce 最后返回的 Promise 實例作為 promise 方法的返回值,則實現 promise 方法執(zhí)行后繼續(xù)調用 then 來實現后續(xù)邏輯。

對其他異步鉤子補充

在上面 Async 異步類型的 “鉤子中”,我們只著重介紹了 “串行” 和 “并行”( AsyncParallelHook 和 AsyncSeriesHook )以及回調和 Promise 的兩種注冊和觸發(fā)事件的方式,還有一些其他的具有一定特點的異步 “鉤子” 我們并沒有進行分析,因為他們的機制與同步對應的 “鉤子” 非常的相似。

AsyncParallelBailHook 和 AsyncSeriesBailHook 分別為異步 “并行” 和 “串行” 執(zhí)行的 “鉤子”,返回值不為 undefined ,即有返回值,則立即停止向下執(zhí)行其他事件處理函數,實現邏輯可結合 AsyncParallelHook 、 AsyncSeriesHook 和 SyncBailHook 。

AsyncSeriesWaterfallHook 為異步 “串行” 執(zhí)行的 “鉤子”,上一個事件處理函數的返回值作為參數傳遞給下一個事件處理函數,實現邏輯可結合 AsyncSeriesHook 和 SyncWaterfallHook 。

總結

在 tapable 源碼中,注冊事件的方法 tab 、 tapSync 、 tapPromise 和觸發(fā)事件的方法 call 、 callAsync 、 promise 都是通過 compile 方法快速編譯出來的,我們本文中這些方法的實現只是遵照了 tapable 庫這些 “鉤子” 的事件處理機制進行了模擬,以方便我們了解 tapable ,為學習 Webpack 原理做了一個鋪墊,在 Webpack 中,這些 “鉤子” 的真正作用就是將通過配置文件讀取的插件與插件、加載器與加載器之間進行連接,“并行” 或 “串行” 執(zhí)行,相信在我們對 tapable 中這些 “鉤子” 的事件機制有所了解之后,再重新學習 Webpack 的源碼應該會有所頭緒。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 晋中市| 灵川县| 镇赉县| 临沧市| 依安县| 南康市| 商水县| 平南县| 双桥区| 罗定市| 岳阳县| 四平市| 丰原市| 湘潭市| 玉田县| 秭归县| 鸡泽县| 古交市| 南城县| 尤溪县| 进贤县| 开化县| 蕲春县| 晋州市| 定襄县| 伊金霍洛旗| 平远县| 剑阁县| 夹江县| 汉阴县| 新丰县| 大埔县| 台州市| 弥勒县| 宣武区| 遂平县| 中牟县| 海阳市| 托克托县| 遂昌县| 香河县|