摘要:本文將根據以下章節分別梳理每個鉤子同步鉤子首先安裝是簡單的同步鉤子,它很類似于發布訂閱。至此,我們把的所有同步鉤子都解析完畢異步鉤子比同步鉤子麻煩些,我們會在下一章節開始解析異步的鉤子傳送門深入理解核心模塊鉤子異步版代碼
記錄下自己在前端路上爬坑的經歷 加深印象,正文開始~
tapable是webpack的核心依賴庫 想要讀懂webpack源碼 就必須首先熟悉tapable
ok.下面是webapck中引入的tapable鉤子 由此可見 在webpack中tapable的重要性
const { SyncHook, SyncBailHook, SyncWaterfallHook, SyncLoopHook, AsyncParallelHook, AsyncParallelBailHook, AsyncSeriesHook, AsyncSeriesBailHook, AsyncSeriesWaterfallHook } = require("tapable");
這些鉤子可分為同步的鉤子和異步的鉤子,Sync開頭的都是同步的鉤子,Async開頭的都是異步的鉤子。而異步的鉤子又可分為并行和串行,其實同步的鉤子也可以理解為串行的鉤子。
本文將根據以下章節分別梳理每個鉤子
同步鉤子
SyncHook
SyncBailHook
SyncWaterfallHook
SyncLoopHook
首先安裝tapable
npm i tapable -D
SyncHook
SyncHook是簡單的同步鉤子,它很類似于發布訂閱。首先訂閱事件,觸發時按照順序依次執行,所以說同步的鉤子都是串行的。
const { SyncHook } = require("tapable"); class Hook{ constructor(){ /** 1 生成SyncHook實例 */ this.hooks = new SyncHook(["name"]); } tap(){ /** 2 注冊監聽函數 */ this.hooks.tap("node",function(name){ console.log("node",name); }); this.hooks.tap("react",function(name){ console.log("react",name); }); } start(){ /** 3出發監聽函數 */ this.hooks.call("call end."); } } let h = new Hook(); h.tap();/** 類似訂閱 */ h.start();/** 類似發布 */ /* 打印順序: node call end. react call end. */
可以看到 它是按照順序依次打印的,其實說白了就是發布和訂閱。接下來我們就手動實現它。
class SyncHook{ // 定義一個SyncHook類 constructor(args){ /* args -> ["name"]) */ this.tasks = []; } /** tap接收兩個參數 name和fn */ tap(name,fn){ /** 訂閱:將fn放入到this.tasks中 */ this.tasks.push(fn); } start(...args){/** 接受參數 */ /** 發布:將this.taks中的fn依次執行 */ this.tasks.forEach((task)=>{ task(...args); }); } } let h = new SyncHook(["name"]); /** 訂閱 */ h.tap("react",(name)=>{ console.log("react",name); }); h.tap("node",(name)=>{ console.log("node",name); }); /** 發布 */ h.start("end."); /* 打印順序: react end. node end. */
SyncBailHook
SyncBailHook 從字面意思上理解為帶有保險的同步的鉤子,帶有保險意思是 根據每一步返回的值來決定要不要繼續往下走,如果return了一個非undefined的值 那就不會往下走,注意 如果什么都不return 也相當于return了一個undefined。
const { SyncBailHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new SyncBailHook(["name"]); } tap(){ this.hooks.tap("node",function(name){ console.log("node",name); /** 此處return了一個非undefined * 代碼到這里就不會繼續執行余下的鉤子 */ return 1; }); this.hooks.tap("react",function(name){ console.log("react",name); }); } start(){ this.hooks.call("call end."); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node call end. */
手動實現
class SyncHook{ constructor(args){ this.tasks = []; } tap(name,fn){ this.tasks.push(fn); } start(...args){ let index = 0; let result; /** 利用do while先執行一次的特性 */ do{ /** 拿到每一次函數的返回值 result */ result = this.tasks[index++](...args); /** 如果返回值不為undefined或者執行完畢所有task -> 中斷循環 */ }while(result === undefined && index < this.tasks.length); } } let h = new SyncHook(["name"]); h.tap("react",(name)=>{ console.log("react",name); return 1; }); h.tap("node",(name)=>{ console.log("node",name); }); h.start("end."); /* 打印順序: react end. */
SyncWaterfallHook
SyncWaterfallHook是同步的瀑布鉤子,瀑布怎么理解呢? 其實就是說它的每一步都依賴上一步的執行結果,也就是上一步return的值就是下一步的參數。
const { SyncWaterfallHook } = require("tapable"); class Hook{ constructor(){ this.hooks = new SyncWaterfallHook(["name"]); } tap(){ this.hooks.tap("node",function(name){ console.log("node",name); /** 此處返回的值作為第二步的結果 */ return "第一步返回的結果"; }); this.hooks.tap("react",function(data){ /** 此處data就是上一步return的值 */ console.log("react",data); }); } start(){ this.hooks.call("callend."); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node callend. react 第一步返回的結果 */
手動實現:
class SyncWaterFallHook{ constructor(args){ this.tasks = []; } tap(name,fn){ this.tasks.push(fn); } start(...args){ /** 解構 拿到tasks中的第一個task -> first */ let [first, ...others] = this.tasks; /** 利用reduce() 累計執行 * 首先傳入第一個 first 并執行 * l是上一個task n是當前task * 這樣滿足了 下一個函數依賴上一個函數的執行結果 */ others.reduce((l,n)=>{ return n(l); },first(...args)); } } let h = new SyncWaterFallHook(["name"]); /** 訂閱 */ h.tap("react",(name)=>{ console.log("react",name); return "我是第一步返回的值"; }); h.tap("node",(name)=>{ console.log("node",name); }); /** 發布 */ h.start("end."); /* 打印順序: react end. node 我是第一步返回的值 */
SyncLoopHook
SyncLoopHook是同步的循環鉤子。 循環鉤子很好理解,就是在滿足一定條件時 循環執行某個函數:
const { SyncLoopHook } = require("tapable"); class Hook{ constructor(){ /** 定義一個index */ this.index = 0; this.hooks = new SyncLoopHook(["name"]); } tap(){ /** 箭頭函數 綁定this */ this.hooks.tap("node",(name) => { console.log("node",name); /** 當不滿足條件時 會循環執行該函數 * 返回值為udefined時 終止該循環執行 */ return ++this.index === 5?undefined:"學完5遍node后再學react"; }); this.hooks.tap("react",(data) => { console.log("react",data); }); } start(){ this.hooks.call("callend."); } } let h = new Hook(); h.tap(); h.start(); /* 打印順序: node callend. node callend. node callend. node callend. node callend. react callend. */
可以看到 執行了5遍node callend后再繼續往下執行。也就是當返回undefined時 才會繼續往下執行:
class SyncWaterFallHook{ constructor(args){ this.tasks = []; } tap(name,fn){ this.tasks.push(fn); } start(...args){ let result; this.tasks.forEach((task)=>{ /** 注意 此處do{}while()循環的是每個多帶帶的task */ do{ /** 拿到每個task執行后返回的結果 */ result = task(...args); /** 返回結果不是udefined時 會繼續循環執行該task */ }while(result !== undefined); }); } } let h = new SyncWaterFallHook(["name"]); let total = 0; /** 訂閱 */ h.tap("react",(name)=>{ console.log("react",name); return ++total === 3?undefined:"繼續執行"; }); h.tap("node",(name)=>{ console.log("node",name); }); /** 發布 */ h.start("end."); /* 打印順序: react end. react end. react end. node end. */
可以看到, 執行了3次react end.后 才繼續執行了下一個task -> node end。至此,我們把tapable的所有同步鉤子都解析完畢. 異步鉤子比同步鉤子麻煩些,我們會在下一章節開始解析異步的鉤子.
傳送門:深入理解Webpack核心模塊Tapable鉤子(異步版)
代碼:mock-webpack-tapable
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102136.html
摘要:接上一篇文章深入理解核心模塊鉤子同步版中三個注冊方法同步注冊的是中對三個觸發方法這一章節我們將分別實現異步的版本和版本異步鉤子的版本的版本的版本異步的鉤子分為并行和串行的鉤子,并行是指等待所有并發的異步事件執行之后再執行最終的異步回調。 接上一篇文章 深入理解Webpack核心模塊WTApable鉤子(同步版) tapable中三個注冊方法 1 tap(同步) 2 tapAsync(...
摘要:引入定義一個自己的插件。一個最基礎的的代碼是這樣的在構造函數中獲取用戶給該插件傳入的配置會調用實例的方法給插件實例傳入對象導出在使用這個時,相關配置代碼如下和在開發時最常用的兩個對象就是和,它們是和之間的橋梁。 本文示例源代碼請戳github博客,建議大家動手敲敲代碼。 webpack本質上是一種事件流的機制,它的工作流程就是將各個插件串聯起來,而實現這一切的核心就是Tapable,w...
摘要:系列文章系列第一篇基礎雜記系列第二篇插件機制雜記系列第三篇流程雜記前言本身并不難,他所完成的各種復雜炫酷的功能都依賴于他的插件機制。的插件機制依賴于一個核心的庫,。是什么是一個類似于的的庫主要是控制鉤子函數的發布與訂閱。 系列文章 Webpack系列-第一篇基礎雜記 Webpack系列-第二篇插件機制雜記 Webpack系列-第三篇流程雜記 前言 webpack本身并不難,他所完成...
摘要:但也是最復雜的一個。中當一旦某個返回值結果不為便結束執行列表中的插件中上一個插件執行結果當作下一個插件的入參調用并行執行插件流程篇本文關于的流程講解是基于的。 webpack是現代前端開發中最火的模塊打包工具,只需要通過簡單的配置,便可以完成模塊的加載和打包。那它是怎么做到通過對一些插件的配置,便可以輕松實現對代碼的構建呢? webpack的配置 const path = requir...
閱讀 1991·2023-04-26 01:41
閱讀 2468·2021-11-24 09:39
閱讀 1922·2021-11-24 09:38
閱讀 1947·2021-11-19 09:40
閱讀 3760·2021-11-11 11:02
閱讀 3294·2021-10-20 13:48
閱讀 3157·2021-10-14 09:43
閱讀 4360·2021-09-02 15:11