We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
webpack本质是事件流机制,工作流程就是将各个插件串联起来,实现这个的核心就是tabable。类似于nodejs的核心也是发布订阅模式。
webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation都是Tapable的实例。
webpack
Compiler
Compilation
Tapable
常用的钩子函数包含以下几种,主要分为同步钩子和异步钩子,异步钩子又分为串行和并行。
const { SyncHook, SyncBailHook, SyncWaterfallHook, SyncLoopHook, AsyncParallelHook, AsyncParallelBailHook, AsyncSeriesHook, AsyncSeriesBailHook, AsyncSeriesWaterfallHook } = require('tapable');
同步:
异步:
并发
AsyncParallelHook 不关心返回值
AsyncParallelBailHook 带保险钩子
串行
AsyncSeriesHook 不关心返回值
AsyncSeriesBailHook
AsyncSeriesWaterfallHook 异步串行瀑布钩子函数
注册方法:
下面具体说说每个hook的功能,以及如何简单实现。
写一个简单的SyncHook同步钩子
let {SyncHook} = require('tapable'); class Dinner { constructor(){ this.hook = new SyncHook(['name']) } // 注册监听 tap(){ this.hook.tap('order1', function(name){ console.log('来订单了 order1,下单人:', name) }) this.hook.tap('order2', function(name){ console.log('来订单了 order2,下单人:', name) }) } // 触发执行 start(){ this.hook.call('sherry') } } const dinner = new Dinner(); dinner.tap(); // 会把注册函数放到一个数组里 dinner.start(); // 触发后把注册的函数依次执行
打印信息:
来订单了 order1,下单人: sherry 来订单了 order2,下单人: sherry
class SyncHook { constructor(){ this.tasks = []; } tap(name, task){ this.tasks.push(task); } call(...args){ // 依次执行注册的函数 this.tasks.forEach(task => { task(...args) }) } } const hook = new SyncHook(); hook.tap('order1', function(name){ console.log('来订单了 order1,下单人:', name) }) hook.tap('order2', function(name){ console.log('来订单了 order1,下单人:', name) }) hook.call('sherry');
写同步时可以加保险,在注册的函数里,可以决定是否向下执行。 返回非undefined的值,不会向下执行了。
let {SyncBailHook} = require('tapable'); class Dinner { constructor(){ this.hook = new SyncBailHook(['name']) } // 注册监听 tap(){ this.hook.tap('order1', function(name){ console.log('来订单了 order1,下单人:', name) return '订单出问题了' // 返回非undefined的值,不会向下执行了 // return undefined // 会向下执行 }) this.hook.tap('order2', function(name){ console.log('来订单了 order2,下单人:', name) }) } // 触发执行 start(){ this.hook.call('sherry') } } const dinner = new Dinner(); dinner.tap(); // 会把注册函数放到一个数组里 dinner.start(); // 触发后把注册的函数依次执行
来订单了 order1,下单人: sherry
class SyncBailHook { constructor() { this.tasks = []; } tap(name, task) { this.tasks.push(task); } call(...args) { let ret; let index = 0; do { ret = this.tasks[index++](...args) } while (ret === undefined && index < this.tasks.length) } } const hook = new SyncBailHook(); hook.tap('order1', function (name) { console.log('来订单了 order1,下单人:', name) return '订单出问题了' // 返回非undefined的值,不会向下执行了 // return undefined // 会向下执行 }) hook.tap('order2', function (name) { console.log('来订单了 order2,下单人:', name) }) hook.call('sherry');
可以传递信息给下一个注册的函数。
let {SyncWaterfallHook} = require('tapable'); class Dinner { constructor(){ this.hook = new SyncWaterfallHook(['name']) } // 注册监听 tap(){ this.hook.tap('order1', function(name){ console.log('订单1 开始配送,配送人:', name) }) this.hook.tap('order2', function(data){ console.log('订单2 开始配送,配送人:', data) return { msg: '这一单超时了,下一单要快点', name: data } }) this.hook.tap('order3', function(data){ console.log('订单3 开始配送,配送人:', data.name, `备注:${data.msg}`) }) } start(){ this.hook.call('sherry') } } const dinner = new Dinner(); dinner.tap(); dinner.start();
订单1 开始配送,配送人: sherry 订单2 开始配送,配送人: sherry 订单3 开始配送,配送人: sherry 备注:这一单超时了,下一单要快点
需要把上一个函数的return的结果,传给下一个函数作为参数。
class SyncWaterfallHook { constructor() { this.tasks = []; } tap(name, task) { this.tasks.push(task); } call(...args) { const [first, ...others] = this.tasks; const ret = first(...args); others.reduce((a, b) => { return a ? b(a) : b(...args) // 如果没有return,还是传默认参数 }, ret) } } const hook = new SyncWaterfallHook(); hook.tap('order1', function (name) { console.log('订单1 开始配送,配送人:', name) }) hook.tap('order2', function (data) { console.log('订单2 开始配送,配送人:', data) return { msg: '这一单超时了,下一单要快点', name: data } }) hook.tap('order3', function (data) { console.log('订单3 开始配送,配送人:', data.name, `备注:${data.msg}`) }) hook.call('sherry');
webpack暂时没有用到这个方法。
若某个注册函数不返回undefined,这个函数及这个函数前面的函数会重复多次执行,直到返回undefined。
let {SyncLoopHook} = require('tapable'); class Dinner { constructor(){ this.index = 0 this.hook = new SyncLoopHook(['name']) } // 注册监听 tap(){ this.hook.tap('order1', function(name){ console.log('订单1 开始配送,配送人:', name) }) this.hook.tap('order1', (name) => { console.log('订单2 开始配送,配送人:', name) this.index ++; return this.index === 3 ? undefined : '外卖订单凉了,请重新配送' }) this.hook.tap('order3', function(name){ console.log('订单3 开始配送,配送人:', name) }) } start(){ this.hook.call('sherry') } } const dinner = new Dinner(); dinner.tap(); dinner.start();
前两个注册函数重复执行了3次,才继续
订单1 开始配送,配送人: sherry 订单2 开始配送,配送人: sherry 订单1 开始配送,配送人: sherry 订单2 开始配送,配送人: sherry 订单1 开始配送,配送人: sherry 订单2 开始配送,配送人: sherry 订单3 开始配送,配送人: sherry
暂时只实现了当前函数重复执行
class SyncLoopHook { constructor() { this.tasks = []; } tap(name, task) { this.tasks.push(task); } call(...args) { this.tasks.forEach(task => { let ret; do { ret = task(...args) } while (ret !== undefined) }) } } const hook = new SyncLoopHook(); let index = 0; hook.tap('order1', function (name) { console.log('订单1 开始配送,配送人:', name) }) hook.tap('order2', function (name) { console.log('订单2 开始配送,配送人:', name) index++; return index === 3 ? undefined : '外卖订单凉了,请重新配送' }) hook.tap('order3', function (name) { console.log('订单3 开始配送,配送人:', name) }) hook.call('sherry');
订单1 开始配送,配送人: sherry 订单2 开始配送,配送人: sherry 订单2 开始配送,配送人: sherry 订单2 开始配送,配送人: sherry 订单3 开始配送,配送人: sherry
异步注册使用tapAsync/tapPromise方法,触发调用使用callAsync/promise方法,需要提供回调函数。
tapAsync
tapPromise
callAsync
promise
所有函数同时执行,等待所有并发的异步事件都执行完了,再执行回调函数。
只有注册的函数都执行了cb(),才会执行回调函数。有任何一个函数没有执行cb(),回调函数都不会执行。
cb()
const {AsyncParallelHook} = require('tapable'); class Dinner { constructor(){ this.index = 0 this.hook = new AsyncParallelHook(['name']) } // 注册监听 tap(){ this.hook.tapAsync('order1', function(name,cb){ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) cb() }, 1000) }) this.hook.tapAsync('order2', (name, cb) => { setTimeout(()=>{ console.log('订单2配送需要2分钟', name) cb() }, 2000) }) } start(){ // 只有注册的函数都执行了`cb()`,才会执行回调函数。有任何一个函数没有执行`cb()`,回调函数都不会执行。 this.hook.callAsync('sherry', ()=>{ console.log('一共配送了2分钟。只有注册函数都执行了cb(), 我才会被执行') }) } } const dinner = new Dinner(); dinner.tap(); dinner.start();
订单1配送需要1分钟 sherry 订单2配送需要2分钟 sherry 一共配送了2分钟。只有注册函数都执行了cb(), 我才会被执行
class AsyncParallelHook { constructor(){ this.tasks = []; } tapAsync(name, task){ this.tasks.push(task); } callAsync(...args){ const finalCallback = args.pop() // 回调函数,都执行完才执行回调函数 let index=0 const done = ()=>{ // 类似于promise.all index++ if(index === this.tasks.length){ finalCallback() } } this.tasks.forEach(task => { task(...args, done) }) } } const hook = new AsyncParallelHook(); let index = 0; hook.tapAsync('order1', function(name, cb){ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) cb() }, 1000) }) hook.tapAsync('order2', function(name, cb){ setTimeout(()=>{ console.log('订单1配送需要2分钟', name) cb() }, 2000) }) hook.callAsync('sherry', ()=>{ console.log('一共配送了2分钟。只有注册函数都执行了cb(), 我才会被执行') });
let {AsyncParallelHook} = require('tapable'); class Dinner { constructor(){ this.index = 0 this.hook = new AsyncParallelHook(['name']) } // 注册监听 tap(){ this.hook.tapPromise('order1', function(name){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) resolve(); }, 1000) }) }) this.hook.tapPromise('order2', (name) => { return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单2配送需要2分钟', name) resolve(); }, 2000) }) }) } start(){ this.hook.promise('sherry').then(()=>{ console.log('结束啦,注册函数都resolve,我才会执行') }) } } const dinner = new Dinner(); dinner.tap(); dinner.start();
订单1配送需要1分钟 sherry 订单2配送需要2分钟 sherry 结束啦,注册函数都resolve,我才会执行
使用Promise.all(tasks)来实现。
Promise.all(tasks)
class AsyncParallelHook { constructor(){ this.tasks = []; } tapPromise(name, task){ this.tasks.push(task); } promise(...args){ let tasks = this.tasks.map(task => task(...args)); return Promise.all(tasks) } } const hook = new AsyncParallelHook(); let index = 0; hook.tapPromise('order1', function(name){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) resolve(); }, 1000) }) }) hook.tapPromise('order2', function(name){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单2配送需要2分钟', name) resolve(); }, 2000) }) }) hook.promise('sherry').then(()=>{ console.log('结束啦,注册函数都resolve,我才会执行') });
注册函数之间有依赖,下一个函数依赖上一个函数返回的结果。
let {AsyncSeriesHook} = require('tapable'); class Dinner { constructor(){ this.hook = new AsyncSeriesHook(['name']) } // 注册监听 tap(){ this.hook.tapAsync('order1', function(name, cb){ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) cb() // 执行cb,才会往下走 },1000) }) this.hook.tapAsync('order2', function(name, cb){ setTimeout(()=>{ console.log('订单2配送需要2分钟', name) cb()// 最后一个函数,执行cb,才会往下走,调用call的回调函数 },2000) }) } start(){ this.hook.callAsync('sherry', () => { console.log('一共执行了3分钟,需要所有注册函数都调用cb(),我才会被执行') }) } } const dinner = new Dinner(); dinner.tap(); dinner.start();
订单1配送需要1分钟 sherry 订单2配送需要2分钟 sherry 一共执行了3分钟,需要所有注册函数都调用cb(),我才会被执行
class AsyncSeriesHook { constructor(){ this.tasks = []; } tapAsync(name, task){ this.tasks.push(task); } callAsync(...args){ const finalcallback = args.pop(); let index = 0; // 这个写法有点像express // 异步迭代,可以使用中间函数来实现 const next = ()=>{ if(this.tasks.length === index){ finalcallback() }else{ let task = this.tasks[index++]; task(...args, next) } } next(); } } const hook = new AsyncSeriesHook(); let index = 0; hook.tapAsync('node', function(name, cb){ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) cb() // 执行cb,才会往下走 }, 1000) }) hook.tapAsync('react', function(name, cb){ setTimeout(()=>{ console.log('订单2配送需要2分钟', name) cb() // 最后一个函数,执行cb,才会往下走,调用call的回调函数 }, 2000) }) hook.callAsync('sherry', () => { console.log('一共执行了3分钟,需要所有注册函数都调用cb(),我才会被执行') });
let {AsyncSeriesHook} = require('tapable'); class Dinner { constructor(){ this.hook = new AsyncSeriesHook(['name']) } // 注册监听 tap(){ this.hook.tapPromise('order1', function(name, cb){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) resolve(); // 执行resolve,才会往下走 }, 1000) }) }) this.hook.tapPromise('order2', function(name, cb){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单2配送需要2分钟', name) resolve(); // 执行resolve,才会往下走 }, 2000) }) }) } start(){ this.hook.promise('sherry').then(()=>{ console.log('结束啦,一共需要3分钟,注册函数都resolve,我才会执行') }) } } const dinner = new Dinner(); dinner.tap(); dinner.start();
订单1配送需要1分钟 sherry 订单2配送需要2分钟 sherry 结束啦,一共需要3分钟,注册函数都resolve,我才会执行
class AsyncSeriesHook { constructor(){ this.tasks = []; } tapPromise(name, task){ this.tasks.push(task); } promise(...args){ const [first, ...others] = this.tasks; // 类似于redux源码 return others.reduce((p, n) => { return p.then(()=>n(...args)) }, first(...args)) } } const hook = new AsyncSeriesHook(); let index = 0; hook.tapPromise('node', function(name){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) resolve(); // 执行resolve,才会往下走 }, 1000) }) }) hook.tapPromise('react', function(name){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log('订单2配送需要2分钟', name) resolve(); // 执行resolve,才会往下走 }, 2000) }) }) hook.promise('sherry').then(() => { console.log('结束啦,一共需要3分钟,注册函数都resolve,我才会执行') });
第一个异步先执行,把结果传给下一个异步执行。若error,会略过后面,直接直接回调。
cb('error'):发生错误,不往下执行,回调函数仍然会执行
cb('error')
cb()/cb(null)/cb(null, data):往下继续执行,可以穿参数给下一个函数
cb()/cb(null)/cb(null, data)
let {AsyncSeriesWaterfallHook} = require('tapable'); class Dinner { constructor(){ this.hook = new AsyncSeriesWaterfallHook(['name']) } // 注册监听 tap(){ this.hook.tapAsync('order1', function(name, cb){ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) cb(undefined, { // 往下执行,并给下一个函数传参 msg: '配送超时,下次快点', name }) // cb('error') // 出错了,不往下执行了 },1000) }) this.hook.tapAsync('order2', function(data, cb){ setTimeout(()=>{ console.log('订单2配送需要2分钟', data.name, `备注:${data.msg}`) cb() },2000) }) } start(){ this.hook.callAsync('sherry', () => { console.log('一共执行了3分钟,需要所有注册函数都调用cb()或者某个函数执行cb("error"),我才会被执行') }) } } const dinner = new Dinner(); dinner.tap(); dinner.start();
订单1配送需要1分钟 sherry 订单2配送需要2分钟 sherry 备注:配送超时,下次快点 一共执行了3分钟,需要所有注册函数都调用cb()或者某个函数执行cb("error"),我才会被执行
若调用cb('error'),打印信息:
订单1配送需要1分钟 sherry 一共执行了3分钟,需要所有注册函数都调用cb()或者某个函数执行cb("error"),我才会被执行
class AsyncSeriesWaterfallHook { constructor(){ this.tasks = []; } tapAsync(name, task){ this.tasks.push(task); } callAsync(...args){ let index=0 const finalCallback = args.pop() // 异步迭代使用中间函数来实现 const next = (err, data) => { let task = this.tasks[index]; if(!task || err) return finalCallback(); // 没有task或报错,执行执行回调函数 if(index === 0){ task(...args, next) // 第一个函数,需要传args }else { task(data, next) // 剩余函数,需要传入上一个函数执行cb的第二个值 } index ++ } next(); } } const hook = new AsyncSeriesWaterfallHook(); hook.tapAsync('order1', function(name, cb){ setTimeout(()=>{ console.log('订单1配送需要1分钟', name) cb(undefined, { // 往下执行,并给下一个函数传参 msg: '配送超时,下次快点', name }) // cb('error') // 出错了,不往下执行了 }, 1000) }) hook.tapAsync('order2', function(data, cb){ setTimeout(()=>{ console.log('订单2配送需要2分钟', data.name, `备注:${data.msg}`) cb() }, 2000) }) hook.callAsync('sherry', ()=>{ console.log('一共执行了3分钟,需要所有注册函数都调用cb()或者某个函数执行cb("error"),我才会被执行') });
The text was updated successfully, but these errors were encountered:
No branches or pull requests
tapable
webpack本质是事件流机制,工作流程就是将各个插件串联起来,实现这个的核心就是tabable。类似于nodejs的核心也是发布订阅模式。
webpack
中最核心的负责编译的Compiler
和负责创建bundles的Compilation
都是Tapable
的实例。hooks概览
常用的钩子函数包含以下几种,主要分为同步钩子和异步钩子,异步钩子又分为串行和并行。
同步:
异步:
并发
AsyncParallelHook 不关心返回值
AsyncParallelBailHook 带保险钩子
串行
AsyncSeriesHook 不关心返回值
AsyncSeriesBailHook
AsyncSeriesWaterfallHook 异步串行瀑布钩子函数
注册方法:
同步Hooks介绍
下面具体说说每个hook的功能,以及如何简单实现。
SyncHook 同步钩子
写一个简单的SyncHook同步钩子
使用示例:
打印信息:
简单实现:
SyncBailHook
写同步时可以加保险,在注册的函数里,可以决定是否向下执行。 返回非undefined的值,不会向下执行了。
使用示例:
打印信息:
简单实现:
SyncWaterfallHook
可以传递信息给下一个注册的函数。
使用示例:
打印信息:
简单实现:
需要把上一个函数的return的结果,传给下一个函数作为参数。
SyncLoopHook
webpack暂时没有用到这个方法。
若某个注册函数不返回undefined,这个函数及这个函数前面的函数会重复多次执行,直到返回undefined。
使用示例:
打印信息:
前两个注册函数重复执行了3次,才继续
简单实现:
暂时只实现了当前函数重复执行
打印信息:
异步Hooks介绍
异步注册使用
tapAsync
/tapPromise
方法,触发调用使用callAsync
/promise
方法,需要提供回调函数。AsyncParallelHook 异步并行
所有函数同时执行,等待所有并发的异步事件都执行完了,再执行回调函数。
使用示例(tapAsync):
只有注册的函数都执行了
cb()
,才会执行回调函数。有任何一个函数没有执行cb()
,回调函数都不会执行。打印信息:
简单实现:
使用示例(tapPromise):
打印信息:
简单实现:
使用
Promise.all(tasks)
来实现。AsyncSeriesHook 异步串行
注册函数之间有依赖,下一个函数依赖上一个函数返回的结果。
使用示例(tapAsync):
打印信息:
简单实现:
使用示例(tapPromise):
打印信息:
简单实现:
AsyncSeriesWaterfallHook
第一个异步先执行,把结果传给下一个异步执行。若error,会略过后面,直接直接回调。
cb('error')
:发生错误,不往下执行,回调函数仍然会执行cb()/cb(null)/cb(null, data)
:往下继续执行,可以穿参数给下一个函数使用示例(tapAsync):
打印信息:
若调用cb('error'),打印信息:
简单实现:
The text was updated successfully, but these errors were encountered: