欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解Webpack中Tapable事件機(jī)制

 更新時(shí)間:2023年11月13日 08:27:55   作者:古茗前端團(tuán)隊(duì)  
Webpack?是前端工程化常用的靜態(tài)模塊打包工具,在合適的時(shí)機(jī)通過?Webpack?提供的?API?改變輸出結(jié)果,使?Webpack?可以執(zhí)行更廣泛的任務(wù),擁有更強(qiáng)的構(gòu)建能力,本文將介紹?Tapable?的基本使用以及底層實(shí)現(xiàn),需要的朋友可以參考下

引言

Webpack 是前端工程化常用的靜態(tài)模塊打包工具。在合適的時(shí)機(jī)通過 Webpack 提供的 API 改變輸出結(jié)果,使 Webpack 可以執(zhí)行更廣泛的任務(wù),擁有更強(qiáng)的構(gòu)建能力。

Webpack 的插件機(jī)制本質(zhì)上是一種事件流的機(jī)制,它的工作流程就是將各個(gè)插件串聯(lián)起來,而實(shí)現(xiàn)這一切的核心就是Tapable,Webpack中最核心的負(fù)責(zé)編譯的Compiler和負(fù)責(zé)創(chuàng)建bundles的Compilation都是Tapable的實(shí)例。

本文將介紹 Tapable 的基本使用以及底層實(shí)現(xiàn)。

Tapable

Tapable 是一個(gè)類似于 Node.js 中的 EventEmitter 的庫,但它更專注于自定義事件的觸發(fā)和處理。通過 Tapable 我們可以注冊自定義事件,然后在適當(dāng)?shù)臅r(shí)機(jī)去執(zhí)行自定義事件。這個(gè)和我們所熟知的生命周期函數(shù)類似,在特定的時(shí)機(jī)去觸發(fā)。

我們先看一個(gè) 簡單 Tapable 的 例子:

const { SyncHook } = require("tapable");

// 實(shí)例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端");

通過上面的例子,我們大致可以將 Tapable 的使用分為以下三步:

  • 實(shí)例化鉤子函數(shù)
  • 事件注冊
  • 事件觸發(fā)

事件注冊

  • 同步的鉤子要用 tap 方法來注冊事件
  • 異步的鉤子可以像同步方式一樣用 tap 方法來注冊,也可以用 tapAsynctapPromise 異步方法來注冊。
    • tapAsync: 使用用 tapAsync 方法來注冊 hook 時(shí),必須調(diào)用callback 回調(diào)函數(shù)。
    • tapPromise:使用 tapPromise 方法來注冊 hook 時(shí),必須返回一個(gè) pormise ,異步任務(wù)完成后 resolve 。

事件觸發(fā)

  • 同步的鉤子要用 call 方法來觸發(fā)
  • 異步的鉤子需要用 callAsyncpromise 異步方法來觸發(fā)。
    • callAsync:當(dāng)我們用 callAsync 方法來調(diào)用 hook 時(shí),第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)的參數(shù)是執(zhí)行任務(wù)的最后一個(gè)返回值
    • promise:當(dāng)我們用 promise 方法來調(diào)用 hook 時(shí),需要使用 then 來處理執(zhí)行結(jié)果,參數(shù)是執(zhí)行任務(wù)的最后一個(gè)返回值。

Tapable Hook 鉤子

tapable 內(nèi)置了 9 種 hook 。分為 同步異步 兩種執(zhí)行方式。異步執(zhí)行 Hook 又可以分為 串行 執(zhí)行和 并行 執(zhí)行。除此之外,hook 可以根據(jù)執(zhí)行機(jī)制分為 常規(guī) 瀑布模式 熔斷模式 循環(huán)模式 四種執(zhí)行機(jī)制。

同步鉤子

同步鉤子顧名思義:同步執(zhí)行,上一個(gè)鉤子執(zhí)行完才會(huì)執(zhí)行下一個(gè)鉤子。

示例代碼如下:

const { SyncHook } = require("tapable");

// 實(shí)例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//該監(jiān)聽函數(shù)有返回值
syncHook.tap("同步鉤子2", (name) => {
  console.log("同步鉤子2", name);
});


//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端");

執(zhí)行結(jié)果如下所示:

異步鉤子

異步鉤子分為: 串行執(zhí)行和并行執(zhí)行。在串行執(zhí)行中,如果上一個(gè)鉤子沒有調(diào)用callback 回調(diào)函數(shù),下一個(gè)鉤子將不會(huì)觸發(fā)對應(yīng)的事件監(jiān)聽。

示例代碼如下:

const { AsyncParallelHook, AsyncSeriesHook } = require("tapable");


const asyncParallelHook = new AsyncParallelHook(["name"]);

const asyncSeriesHook = new AsyncSeriesHook(["name"]);


//通過tap函數(shù)注冊事件
asyncParallelHook.tapAsync("異步并行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("異步并行鉤子1", name);
  }, 3000);
});

//該監(jiān)聽函數(shù)有返回值
asyncParallelHook.tapAsync("異步并行鉤子2", (name, callback) => {
  setTimeout(() => {
    console.log("異步并行鉤子2", name);
  }, 1500);
});

//通過tap函數(shù)注冊事件
asyncSeriesHook.tapAsync("異步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("異步串行鉤子1", name);
  }, 3000);
});

//該監(jiān)聽函數(shù)有返回值
asyncSeriesHook.tapAsync("異步串行鉤子2", (name, callback) => {
  setTimeout(() => {
    console.log("異步串行鉤子2", name);
  }, 1500);
});


// 異步并行鉤子 通過 callAsync 發(fā)布事件
asyncParallelHook.callAsync("古茗前端", () => {
  console.log("1111");
  return "1122";
});

// 異步串行鉤子 通過 callAsync 發(fā)布事件
asyncSeriesHook.callAsync("古茗前端", () => {
  console.log("1111");
  return "1122";
});

控制臺(tái)輸出結(jié)果如下圖所示:

串行鉤子1沒有調(diào)用callback, 所以串行鉤子2沒有觸發(fā)。添加callback后,控制臺(tái)輸出結(jié)果:

熔斷類

AsyncSeriesBailHook 是一個(gè)異步串行、熔斷類型的 Hook。在串行的執(zhí)行過程中,只要其中一個(gè)有返回值,后面的就不會(huì)執(zhí)行了。

示例代碼如下:

const { SyncBailHook, AsyncSeriesBailHook } = require("tapable");

const syncBailHook = new SyncBailHook(["name"]);
const asyncSeriesBailHook = new AsyncSeriesBailHook(["name"]);

syncBailHook.tap("保險(xiǎn)類同步鉤子1", (name) => {
  console.log("保險(xiǎn)類同步鉤子1", name);
});

syncBailHook.tap("保險(xiǎn)類同步鉤子2", (name) => {
  console.log("保險(xiǎn)類同步鉤子2", name);
  return "有返回值";
});

syncBailHook.tap("保險(xiǎn)類同步鉤子3", (name) => {
  console.log("保險(xiǎn)類同步鉤子3", name);
});

asyncSeriesBailHook.tapAsync("保險(xiǎn)類異步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("保險(xiǎn)類異步串行鉤子1", name);
    callback();
  }, 3000);
});

asyncSeriesBailHook.tapAsync("保險(xiǎn)類2步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("保險(xiǎn)類異步串行鉤子2", name);
    callback("有返回值");
  }, 2000);
});

asyncSeriesBailHook.tapAsync("保險(xiǎn)類異步串行鉤子3", (name) => {
  setTimeout(() => {
    console.log("保險(xiǎn)類異步串行鉤子3", name);
  }, 1000);
});

syncBailHook.call("古茗前端");
asyncSeriesBailHook.callAsync("古茗前端", (result) => {
  console.log("result", result);
});

控制臺(tái)輸出結(jié)果如下圖所示:

循環(huán)類

SyncLoopHook 是一個(gè)同步、循環(huán)類型的 Hook。循環(huán)類型的含義是不停的循環(huán)執(zhí)行事件函數(shù),直到所有函數(shù)結(jié)果 result === undefined,不符合條件就調(diào)頭重新開始執(zhí)行。

示例代碼:

const { SyncLoopHook } = require("tapable");

const syncLoopHook = new SyncLoopHook(["name"]);

let count = 4;

syncLoopHook.tap("循環(huán)鉤子1", (name) => {
  console.log("循環(huán)鉤子1", count);
  return count <= 3 ? undefined : count--;
});

syncLoopHook.tap("循環(huán)鉤子2", (name) => {
  console.log("循環(huán)鉤子2", count);
  return count <= 2 ? undefined : count--;
});

syncLoopHook.tap("循環(huán)鉤子3", (name) => {
  console.log("循環(huán)鉤子3", count);
  return count <= 1 ? undefined : count--;
});


syncLoopHook.call();

控制臺(tái)輸出結(jié)果:

瀑布類

AsyncSeriesWaterfallHook 是一個(gè)異步串行、瀑布類型的 Hook。如果前一個(gè)事件函數(shù)的結(jié)果 result !== undefined,則 result 會(huì)作為后一個(gè)事件函數(shù)的第一個(gè)參數(shù)(也就是上一個(gè)函數(shù)的執(zhí)行結(jié)果會(huì)成為下一個(gè)函數(shù)的參數(shù))。

示例代碼:

const { AsyncSeriesWaterfallHook, SyncWaterfallHook } = require("tapable");

const syncWaterfallHook = new SyncWaterfallHook(["name"]);

const asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook(["name"]);

syncWaterfallHook.tap("瀑布式同步鉤子1", (name) => {
  console.log("瀑布式同步鉤子1", name);

  return "古茗前端1";
});

syncWaterfallHook.tap("瀑布式同步鉤子2", (name) => {
  console.log("瀑布式同步鉤子2", name);
});

syncWaterfallHook.tap("瀑布式同步鉤子3", (name) => {
  console.log("瀑布式同步鉤子3", name);

  return "古茗前端3";
});

asyncSeriesWaterfallHook.tapAsync("瀑布式異步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("瀑布式異步串行鉤子1", name);

    callback();
  }, 1000);
});

asyncSeriesWaterfallHook.tapAsync("瀑布式異步串行鉤子2", (name, callback) => {
  console.log("瀑布式異步串行鉤子2", name);
  setTimeout(() => {
    callback();
  }, 2000);
});

asyncSeriesWaterfallHook.tapAsync("瀑布式異步串行鉤子3", (name, callback) => {
  console.log("瀑布式異步串行鉤子3", name);
  setTimeout(() => {
    callback("古茗前端3");
  }, 3000);
});

syncWaterfallHook.call("古茗前端");

asyncSeriesWaterfallHook.callAsync("古茗前端", (result) => {
  console.log("result", result);
});


控制臺(tái)輸出結(jié)果:

Tapable 高級(jí)特性

Intercept

除了通常的 tap/call 之外,所有 hook 鉤子都提供額外的攔截API。— intercept 接口。

intercept 支持的中間件如下圖所示:

intercept類型描述
call(...args) => void 當(dāng)鉤子被觸發(fā)時(shí),向攔截器添加調(diào)用將被觸發(fā)。您可以訪問hooks參數(shù)
tap(tap: Tap) 將tap添加到攔截器將在插件點(diǎn)擊鉤子時(shí)觸發(fā)。提供的是Tap對象。無法更改Tap對象
loop(...args) => void 向攔截器添加循環(huán)將觸發(fā)循環(huán)鉤子的每個(gè)循環(huán)。
register(tap: Tap) => Tap 或 undefined將注冊添加到攔截器將觸發(fā)每個(gè)添加的Tap,并允許對其進(jìn)行修改。

context

插件和攔截器可以選擇訪問可選的上下文對象,該對象可用于向后續(xù)插件和攔截器傳遞任意值。

我們通過下面的小案例,來幫助我們理解。示例代碼如下:

const { SyncHook } = require("tapable");

// 實(shí)例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);

syncHook.intercept({
  context: true,
  register(context, name) {
    console.log("every time tap", context, name);
  },
  call(context, name) {
    console.log("before call", context, name);
  },
  loop(context, name) {
    console.log("before loop", context, name);
  },
  tap(context, name) {
    console.log("before tap", context, name);
  },
});

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子2", (name) => {
  console.log("同步鉤子2", name);
});

//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端");
syncHook.call("古茗前端 call2");

控制臺(tái)輸入結(jié)果如圖所示:

由上面的案例結(jié)果,我們可以知道。intercept 中的 register 會(huì)在每一次的 tap 觸發(fā)。 有幾個(gè) tap 就會(huì)觸發(fā)幾次 register。然后依次執(zhí)行鉤子里面的 call、tap.

intercept 特性在 webpack 內(nèi)主要被用作進(jìn)度提示,如 webpack/lib/ProgressPlugin 插件中,分別對 compiler.hooks.emit 、compiler.hooks.afterEmit 鉤子應(yīng)用了記錄進(jìn)度的中間件函數(shù)。

HookMap

HookMap HookMap是具有Hooks的Map的輔助類.提供了一種集合操作能力,能夠降低創(chuàng)建與使用的復(fù)雜度,用法比較簡單:

const { SyncHook, HookMap } = require("tapable"); 
const syncMap = new HookMap(() => new SyncHook()); 

// 通過 for 函數(shù)過濾集合中的特定鉤子 
syncMap.for("some-key").tap("MyPlugin", (arg) => { /* ... */ });
syncMap.for("some-key").tapAsync("MyPlugin", (arg, callback) => { /* ... */ });
syncMap.for("some-key").tapPromise("MyPlugin", (arg) => { /* ... */ });

// 觸發(fā) guming-test 類型的鉤子 
syncMap.get("guming-test").call();

Tapable 底層原理

我們先將 Tapable 工程源碼克隆到本地, 執(zhí)行如下指令:

$ git clone https://github.com/webpack/tapable.git

Tapable 源碼的 lib 目錄結(jié)構(gòu), 如下所示:

lib
├─ AsyncParallelBailHook.js
├─ AsyncParallelHook.js
├─ AsyncSeriesBailHook.js
├─ AsyncSeriesHook.js
├─ AsyncSeriesLoopHook.js
├─ AsyncSeriesWaterfallHook.js
├─ Hook.js
├─ HookCodeFactory.js
├─ HookMap.js
├─ MultiHook.js
├─ SyncBailHook.js
├─ SyncHook.js
├─ SyncLoopHook.js
├─ SyncWaterfallHook.js
├─ index.js
└─ util-browser.js

除了上面我們所提及的基本 hooks 函數(shù)、HookMap高級(jí)特性,還會(huì)有一些 HookCodeFactory、Hook 這些文件。我們簡單過一下 hooks 函數(shù)內(nèi)的內(nèi)容, 會(huì)發(fā)現(xiàn)所有的 hooks 函數(shù)都會(huì)引用 HookCodeFactoryHook 這兩個(gè)文件所導(dǎo)出的對象實(shí)例。

我們以 syncHook 鉤子函數(shù)為例, 如下圖所示:

我們大致能夠知道 一個(gè) hooks 函數(shù) 會(huì)由一個(gè) CodeFactory 代碼工廠 以及 Hook 實(shí)例組成。 Hook 實(shí)例會(huì)針對不同場景的 hooks 函數(shù), 更改其對應(yīng)的 注冊鉤子(tapAsync ,tap, tapPromise ),事件觸發(fā)鉤子( call , callAsync ), 編譯函數(shù)(complier)。 Complier 函數(shù)會(huì)由我們 HookCodeFactory 實(shí)現(xiàn)。

接下來,我們將通過分析 HookCodeFactoryHook 的內(nèi)部實(shí)現(xiàn)來了解 Tapable 的內(nèi)部實(shí)現(xiàn)機(jī)制。

Hook 實(shí)例

Hook 實(shí)例會(huì)生成我們 hooks 鉤子函數(shù)通用的 事件注冊,事件觸發(fā)。核心邏輯,我們大致可以分為以下三個(gè)部分:

  • 實(shí)例初始化構(gòu)造函數(shù)
  • 事件注冊 的實(shí)現(xiàn)
  • 事件觸發(fā)的實(shí)現(xiàn)

構(gòu)造函數(shù)

構(gòu)造函數(shù)會(huì)對實(shí)例屬性初始化賦值。代碼如下圖所示:

注冊事件

注冊事件主要分為兩塊,一塊是 適配器注冊調(diào)用, 第二塊是 觸發(fā)事件注冊。核心邏輯在 _tap 函數(shù)內(nèi)部實(shí)現(xiàn),代碼如下圖所示:

適配器調(diào)用

在這里會(huì)對攜帶 register 函數(shù)的適配器進(jìn)行調(diào)用,更改 options 配置,返回新的 options 配置。代碼如下圖所示:

觸發(fā)事件注冊

Hook 實(shí)例的 taps 會(huì)存儲(chǔ)我們的注冊事件, 同時(shí)會(huì)根據(jù),注冊事件配置的執(zhí)行順序去存儲(chǔ)對應(yīng)的注冊事件。

觸發(fā)事件

觸發(fā)事件會(huì)通過調(diào)用內(nèi)部 的 _createCall 函數(shù),函數(shù)內(nèi)部會(huì)調(diào)用實(shí)例的 compile 函數(shù)。我們會(huì)發(fā)現(xiàn):Hook 實(shí)例內(nèi)部不會(huì)去實(shí)現(xiàn) complier的邏輯, 不同鉤子的 complier 函數(shù)會(huì)通過通過對應(yīng)的 繼承 HookCodeFactory 的實(shí)例去實(shí)現(xiàn)。代碼如下圖所示:

接下來,我們繼續(xù) 探究 HookCodeFactory 實(shí)例,了解 Tapable 事件觸發(fā)的邏輯。

HookCodeFactory 實(shí)例

HookCodeFactory 實(shí)例會(huì)根據(jù)我們傳入的事件觸發(fā)類型 (sync, async, promise)以及我們的觸發(fā)機(jī)制類型 (常規(guī) 瀑布模式 保險(xiǎn)模式 循環(huán)模式), 生成事件觸發(fā)函數(shù)的函數(shù)頭, 函數(shù)體。通過 new Function 構(gòu)造出事件觸發(fā)函數(shù)。

Tapable 事件觸發(fā)的執(zhí)行,是動(dòng)態(tài)生成執(zhí)行代碼, 包含我們的參數(shù),函數(shù)頭,函數(shù)體,然后通過 new Function 來執(zhí)行。相較于我們通常的遍歷/遞歸調(diào)用事件,這無疑讓 webpack 的整個(gè)事件機(jī)制的執(zhí)行有了一個(gè)更高的性能優(yōu)勢。

由上面我們可知, Hook 實(shí)例 的 complier 函數(shù)是 HookCodeFactory 實(shí)例 create 函數(shù) 的返回。

接下來,我們就從 create 函數(shù) 一步步揭秘 Tapable 的動(dòng)態(tài)生成執(zhí)行函數(shù)的核心實(shí)現(xiàn)。

create

create 函數(shù)通過對應(yīng)的 函數(shù)參數(shù), 函數(shù) header, 函數(shù) content方法構(gòu)造出我們事件觸發(fā)的函數(shù)的內(nèi)容, 通過 new Function 創(chuàng)建出我們的觸發(fā)函數(shù)。

create 函數(shù)會(huì)根據(jù)事件的觸發(fā)類型 ( sync、async、promise),進(jìn)行不同的邏輯處理。代碼如下圖所示:

每一種觸發(fā)機(jī)制,都會(huì)由 this.argsthis.headerthis.contentWithInterceptors 三個(gè)函數(shù)去實(shí)現(xiàn) 動(dòng)態(tài)函數(shù)的 code。代碼如下圖所示:

接下來我們看一看 this.contentWithInterceptors 函數(shù)如何生成我們事件觸發(fā)函數(shù)的函數(shù)體。

contentWithInterceptors

contentWithInterceptors 函數(shù)里包含兩個(gè)模塊, 一個(gè)是適配器 (interceptor), 一個(gè) content 生成函數(shù)。 同時(shí),HookCodeFactory 實(shí)例本身不會(huì)去實(shí)現(xiàn) content 函數(shù)的邏輯,會(huì)由繼承的實(shí)例去實(shí)現(xiàn)。整體結(jié)構(gòu)代碼如下圖所示:

每個(gè) hooks 鉤子函數(shù)的 CodeFactory 實(shí)例會(huì)去實(shí)現(xiàn) content 函數(shù)。 content 函數(shù)會(huì)調(diào)用 HookCodeFactory 實(shí)現(xiàn)的不同運(yùn)行機(jī)制的方法( callTap、callTapsSeries、callTapsLooping、callTapsParallel), 構(gòu)造出最終的函數(shù)體。實(shí)現(xiàn)代碼如下圖所示:

接下來,就是不同運(yùn)行機(jī)制,根據(jù)不同的調(diào)用方式 ( syncasyncpromise ) 生成對應(yīng)的執(zhí)行代碼。

動(dòng)態(tài)函數(shù)

我們通過下面一個(gè)簡單案例,看 New Function 輸入的內(nèi)容是什么?

實(shí)例代碼如下:

const { SyncHook, AsyncSeriesHook } = require("tapable");

// 實(shí)例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);
const asyncSeriesHook = new AsyncSeriesHook(["name"])

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//通過tap函數(shù)注冊事件
asyncSeriesHook.tapAsync("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端sync");
asyncSeriesHook.callAsync("古茗前端async");
asyncSeriesHook.promise("古茗前端promise")

在 HookCodeFactory 的 create 打印 fn, 實(shí)例代碼如圖所示:

sync 同步調(diào)用的輸出結(jié)果如下:

 function anonymous(name
) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(name);

}

async 異步調(diào)用的輸出結(jié)果如下:

function anonymous(name, _callback
) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(name, (function(_err0) {
if(_err0) {
_callback(_err0);
} else {
_callback();
}
}));

}

promise 調(diào)用的輸出結(jié)果如下

 function anonymous(name
) {
"use strict";
var _context;
var _x = this._x;
return new Promise((function(_resolve, _reject) {
var _sync = true;
function _error(_err) {
if(_sync)
_resolve(Promise.resolve().then((function() { throw _err; })));
else
_reject(_err);
};
var _fn0 = _x[0];
_fn0(name, (function(_err0) {
if(_err0) {
_error(_err0);
} else {
_resolve();
}
}));
_sync = false;
}));

}

三種不同方式調(diào)用,內(nèi)部代碼實(shí)現(xiàn)差異還是比較清晰的。async 調(diào)用相較于 sync 多了回調(diào)函數(shù)。 asyncpromise 的區(qū)別再去返回 promise 還是回調(diào)函數(shù)。

最后,我們來用一些結(jié)構(gòu)圖來總結(jié)一下 Tapable 中事件觸發(fā)的邏輯。

HookCodeFactory 會(huì)根據(jù)我們觸發(fā)的方式,生成我們對應(yīng) new Function 里面的 content, args, header。

content 最終會(huì)由 callTapsSeriescallTapsLooping、callTapsParallel 生成。每種生成方式都會(huì)包含 Done 處理、Error 處理以及 Result 處理。

總結(jié)

其他機(jī)制的 Hooks 鉤子實(shí)現(xiàn)原理大致是相同的, 這里就不一一贅述了。Tapable 是一個(gè)非常優(yōu)秀的庫,靈活擴(kuò)展性高,許多優(yōu)秀的開源項(xiàng)目的插件化設(shè)計(jì)都借鑒或采納了 Tapable 的設(shè)計(jì)思想,是一個(gè)非常值得推薦學(xué)習(xí)的一個(gè)開源工具庫。

以上就是一文詳解Webpack中Tapable事件機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Webpack Tapable事件機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 利用百度地圖API獲取當(dāng)前位置信息的實(shí)例

    利用百度地圖API獲取當(dāng)前位置信息的實(shí)例

    下面小編就為大家?guī)硪黄冒俣鹊貓DAPI獲取當(dāng)前位置信息的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望對大家有所幫助
    2017-11-11
  • Cocos2d實(shí)現(xiàn)刮刮卡效果

    Cocos2d實(shí)現(xiàn)刮刮卡效果

    這篇文章主要為大家詳細(xì)介紹了Cocos2d實(shí)現(xiàn)刮刮卡效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • JavaScript中的this關(guān)鍵字用法詳解

    JavaScript中的this關(guān)鍵字用法詳解

    在編寫JavaScript應(yīng)用的時(shí)候,我們經(jīng)常會(huì)使用this關(guān)鍵字。那么this關(guān)鍵字究竟是怎樣工作的,它的設(shè)計(jì)有哪些好的地方,有哪些不好的地方,本文帶大家全面系統(tǒng)地認(rèn)識(shí)這個(gè)老朋友,感興趣的小伙伴可以借鑒閱讀
    2023-05-05
  • TypeScript中的interface與type實(shí)戰(zhàn)

    TypeScript中的interface與type實(shí)戰(zhàn)

    這篇文章主要介紹了TypeScript中的interface與type詳解,它們都是用來定義類型的強(qiáng)大工具,在實(shí)際開發(fā)中,你可以根據(jù)具體情況選擇使用 interface 或 type,或者甚至將它們結(jié)合起來使用,需要的朋友可以參考下
    2023-06-06
  • PhotoSwipe異步動(dòng)態(tài)加載圖片方法

    PhotoSwipe異步動(dòng)態(tài)加載圖片方法

    這篇文章主要為大家詳細(xì)介紹了PhotoSwipe異步動(dòng)態(tài)加載圖片方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • echarts地圖設(shè)置背景圖片及海岸線實(shí)例代碼

    echarts地圖設(shè)置背景圖片及海岸線實(shí)例代碼

    公司的業(yè)務(wù)涉及到統(tǒng)計(jì)圖的有很多,最近一直echarts里面踩各種坑,下面這篇文章主要給大家介紹了關(guān)于echarts地圖設(shè)置背景圖片及海岸線的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • 前端技巧之HTTP中POST提交數(shù)據(jù)四種方式

    前端技巧之HTTP中POST提交數(shù)據(jù)四種方式

    這篇文章主要為大家介紹了前端技巧之HTTP中POST提交數(shù)據(jù)四種方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • 過期軟件破解辦法實(shí)例詳解

    過期軟件破解辦法實(shí)例詳解

    這篇文章主要介紹了過期軟件破解辦法實(shí)例詳解的相關(guān)資料,5行腳本代碼即可改變軟件期限,具有參考價(jià)值,其他軟件可參考此方法,需要的朋友可以參考下
    2017-01-01
  • 異步j(luò)avascript的原理和實(shí)現(xiàn)技巧介紹

    異步j(luò)avascript的原理和實(shí)現(xiàn)技巧介紹

    因?yàn)楣ぷ鞯男枰?,我要在網(wǎng)頁端編寫一段腳本,把數(shù)據(jù)通過網(wǎng)頁批量提交到系統(tǒng)中去。所以我就想到了Greasemonkey插件,于是就開始動(dòng)手寫,發(fā)現(xiàn)問題解決得很順利
    2012-11-11
  • 再談JavaScript線程

    再談JavaScript線程

    繼上篇討論了一些關(guān)于JavaScript線程的知識(shí),我們不妨回過頭再看看,是不是JavaScript就不能多線程呢?看下面一段很簡單的代碼(演示用,沒考慮兼容問題):
    2015-07-07

最新評(píng)論