当前位置: 首页 > 后端技术 > Node.js

深入理解WebpackTapablehook核心模块【同步版】

时间:2023-04-03 14:56:07 Node.js

记录下自己在前端路上爬坑的经历加深印象,正文开始~tapable是webpack的核心依赖库如果想要了解webpack的源码,首先要熟悉tapableok。它是webpack中引入的tapablehook。由此可见tapable在webpack中的重要性。const{SyncHook,SyncBailHook,SyncWaterfallHook,SyncLoopHook,AsyncParallelHook,AsyncParallelBailHook,AsyncSeriesHook,AsyncSeriesBailHook,AsyncSeriesWaterfallHook}=tapable)(";同步钩子和异步钩子,Sync开头的都是同步钩子,Async开头的都是异步hooks,异步hooks分为并行和串行,其实同步hooks也可以理解为串行hooks,本文将按照后面的章节分别梳理各个hooksSyncHookSyncBailHookSyncWaterfallHookSyncLoopHook首先安装tapablenpmitapable-DSyncHookSyncHookis一个简单的同步钩子,和发布订阅很像,先订阅事件,触发时顺序执行,所以同步钩子都是串行的。const{SyncHook}=require('tapable');classHook{constructor(){/**1生成SyncHook实例*/this.hooks=newSyncHook(['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('callend.');}}leth=newHook();h.tap();/**类似于订阅*/h.start();/**类似于发布*//*打印顺序:节点调用结束。reactcallend.*/可以看出是顺序打印,其实就是发布和订阅。接下来我们手动实现。classSyncHook{//定义一个SyncHook类构造函数(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);});}}leth=newSyncHook(['name']);/**订阅*/h.tap('react',(name)=>{console.log('react',name);});h.tap('node',(name)=>{console.log('node',name);});/**Release*/h.start('end.');/*打印命令:reactend.nodeend.*/SyncBailHookSyncBailHook字面意思是带保险同步钩子,withinsurance的意思是根据每一步返回的值来决定是否继续往下走。如果返回非undefined值,则不会往下走。注意,如果什么都没有返回,则相当Finallyreturnedanundefined。const{SyncBailHook}=require('tapable');classHook{constructor(){this.hooks=newSyncBailHook(['name']);}tap(){this.hooks.tap('node',function(name){console.log('node',name);/**这里返回一个非undefined*这里的代码不会继续执行剩下的钩子*/return1;});this.hooks.tap('react',function(name){console.log('react',name);});}start(){this.hooks.call('通话结束');}}leth=newHook();h.tap();h.start();/*打印顺序:nodecallend.*/手动实现classSyncHook{constructor(args){this.tasks=[];}tap(name,fn){这个.tasks.push(fn);}开始(...args){让索引=0;让结果;/**利用dowhile的特性先执行*/do{/**获取每个函数的返回值result*/result=this.tasks[index++](...args);/**如果返回值不是undefined或者所有任务都执行完了->中断循环*/}while(result===undefined&&index{console.log('react',name);return1;});h.tap('node',(name)=>{console.log('node',name);});h.start('end.');/*打印顺序:reactend.*/SyncWaterfallHookSyncWaterfallHook是一个同步瀑布hook,你是怎么理解瀑布的?其实它的每一步都依赖于上一步的执行结果,即上一步的返回值是下一步的参数const{SyncWaterfallHook}=require('tapable');classHook{constructor(){this.hooks=newSyncWaterfallHook(['name']);}tap(){this.hooks.tap('node',function(name){console.log('node',name);/**这里返回的值是第二步的结果*/return'the第一步返回的结果';});this.hooks.tap('react',function(data){/**这里的data是上一步返回的值*/console.log('react',data);});}start(){this.hooks.call('callend.');}}leth=newHook();h.tap();h.start();/*打印顺序:nodecallend.react第一步返回的结果*/手动实现:classSyncWaterFallHook{constructor(args){this.tasks=[];}tap(name,fn){this.tasks.push(fn);}start(...args){/**解构得到tasks中的第一个任务->first*/let[first,...others]=this.tasks;/**使用reduce()累计执行*first传入firstfirst并执行*l为上一个任务n为当前任务*这个满足下一个函数依赖于上一个函数的执行结果*/others.r归纳((l,n)=>{返回n(l);},first(...args));}}leth=newSyncWaterFallHook(['name']);/**订阅*/h.tap('react',(name)=>{console.log('react',name);return'我是第一步返回的值';});h.tap('node',(name)=>{console.log('node',name);});/**release*/h.start('end.');/*打印顺序:reactend.node我是第一个返回的值step*/SyncLoopHookSyncLoopHook是同步循环挂钩循环钩子很容易理解,就是在满足一定条件的情况下循环执行某个函数:const{SyncLoopHook}=require('tapable');classHook{constructor(){/**定义一个索引*/this.index=0;this.hooks=newSyncLoopHook(['name']);}tap(){/**箭头函数绑定this*/this.hooks.tap('node',(name)=>{console.log('node',name);/**当不满足条件时,函数会循环执行*当返回值为undefined时,循环执行结束*/return++this.index===5?undefined:'Afterlearning5timesLearnreactafternode';});this.hooks.tap('react',(data)=>{console.log('react',data);});}start(){this.hooks.call('callend.');}}leth=newHook();h.tap();h.start();/*打印顺序:nodecallend.nodecallend.nodecallend.nodecallend.nodecallend.reactcallend.*/可以看到该节点callend已经执行了5次才继续执行。即当返回undefined时,会继续执行:classSyncWaterFallHook{constructor(args){this.tasks=[];}tap(name,fn){this.tasks.push(fn);}开始(...args){让结果;this.tasks.forEach((task)=>{/**注意这里的do{}while()循环是每个单独的任务*/do{/**获取每个任务执行后返回的结果*/result=task(...args);/**当返回结果不为undefined时,任务会继续循环执行*/}while(result!==undefined);});}}leth=newSyncWaterFallHook(['name']);lettotal=0;/**subscribe*/h.tap('react',(name)=>{console.log('react',name);return++total===3?undefined:'继续执行';});h.tap('node',(name)=>{console.log('node',name);});/**Publish*/h.start('end.');/*打印顺序:反应结束。反应结束。反应结束。nodeend.*/可以看到reactend之后执行了下一个任务。执行3次->节点结束。至此,我们已经分析完了tapable的所有同步钩子。异步钩子比同步钩子更麻烦。我们将在下一章开始分析异步钩子。传送门:深入理解Webpack核心模块Tapablehook(异步版)代码:mock-webpack-tapable