内容简介:Tapable 是webpack中的基础类,类似于node中的EventEmitter,都是注册监听,然后收发事件,监听函数执行的过程,自身可以被继承或混入到其它模块中。webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation都是Tapable的实例。所以如果你想了解webpack的源码,那么先来了解一下Tapable这个基础类显得尤为必要,那接下来让
Tapable 是webpack中的基础类,类似于node中的EventEmitter,都是注册监听,然后收发事件,监听函数执行的过程,自身可以被继承或混入到其它模块中。
webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation都是Tapable的实例。
所以如果你想了解webpack的源码,那么先来了解一下Tapable这个基础类显得尤为必要,那接下来让我带你先来了解一下这个类上的常用的 9个钩子函数吧!
安装
npm i Tapable -d 复制代码
hooks分类
常用的钩子主要包含以下几种,分为同步和异步,异步又分为并发执行和串行执行,如下图:
hooks详解
每一个钩子都是一个构造函数,所有的构造函数都接收一个可选的参数 这个参数是一个数组,数组里面可以放一些参数 例如:‘name’,当触发hook事件时 需要传入name 参数,然后监听函数中可以获取name参数。
const hook = new Hook([‘name’]) 复制代码
那怎么注册事件呢?
根据不同的钩子函数使用不同的方法注册事件,常用的注册事件的方法有:tap, tapPromise, tapAsync。
那怎么触发事件呢?
同样的,也是根据不同的钩子函数 使用的触发事件的方法也不相同,常用的触发事件的方法有:call,promise, callAsync。
Sync*类型的hooks
注册在该钩子下面的插件的执行顺序都是顺序执行。 只能使用tap注册,不能使用tapPromise和tapAsync注册
1、SyncHook
串行同步执行 不关心返回值 复制代码
用法
const { SyncHook } = require('tapable'); const mySyncHook = new SyncHook(['name', 'age']); // 为什么叫tap水龙头 接收两个参数,第一个参数是名称(备注:没有任何意义) 第二个参数是一个函数 接收一个参数 name这个name和上面的name对应 age和上面的age对应 mySyncHook.tap('1', function (name, age) { console.log(name, age, 1) return 'wrong' // 不关心返回值 这里写返回值对结果没有任何影响 }); mySyncHook.tap('2', function (name, age) { console.log(name, age, 2) }); mySyncHook.tap('3', function (name, age) { console.log(name, age, 3) }); mySyncHook.call('liushiyu', '18'); // 执行的结果 // liushiyu 18 1 // liushiyu 18 2 // liushiyu 18 3 复制代码
SyncHook源码大致实现
class SyncHook { constructor () { this.hooks = []; } tap (name, fn) { this.hooks.push(fn) } call () { this.hooks.forEach(hook => hook(...arguments)); } } 复制代码
2、SyncBailHook
串行同步执行 有一个返回值不为null就跳过剩下的逻辑 Bail 是保险的意思 有一个出错就不往下执行了 复制代码
用法 同SyncHook 的用法
const { SyncBailHook } = require('tapable'); const mySyncBailHook = new SyncBailHook(['name', 'age']); // 输出结果 // liushiyu 18 1 // return 的值不是null 所以剩下的逻辑就不执行了 复制代码
SyncBailHook源码大致实现
class SyncBailHook { constructor () { this.hooks = []; } tap (name, fn) { this.hooks.push(fn) } call () { for(let i=0; i<this.hooks.length; i++) { let hook = this.hooks[i]; let result = hook(...arguments); if (result) { break; } } } } 复制代码
3、SyncWaterfallHook
下一个任务要拿到上一个任务的返回值 复制代码
用法
const { SyncWaterfallHook } = require('tapable'); const mySyncWaterfallHook = new SyncWaterfallHook(['name']); mySyncWaterfallHook.tap('1', function (name) { console.log(name, '1') return '1' }) mySyncWaterfallHook.tap('2', function (name) { console.log(name, '2') return '2' }) mySyncWaterfallHook.tap('3', function (name) { console.log(name, '3') }) mySyncWaterfallHook.call('liu') // 输出结果 // liu 1 // 1 2 // 2 3 复制代码
SyncWaterfallHook源码大致实现
class SyncWaterfallHook { constructor () { this.hooks = []; } tap (name, fn) { this.hooks.push(fn) } call () { let result = null for(let i=0; i< this.hooks.length; i++) { let hook = this.hooks[i]; if (!i) { result = hook(...arguments) } else { result = hook(result) } } } } 复制代码
4、SyncLoopHook
监听函数返回true表示继续循环,返回undefine表示结束循环 复制代码
用法
const { SyncLoopHook } = require('tapable'); const mySyncLoopHook = new SyncLoopHook(['name']); let count = 0; mySyncLoopHook.tap('1', function (name) { console.log(count++); if (count < name) { return true } else { return } }); mySyncLoopHook.call('4'); // 输出结果 //0 //1 //2 //3 复制代码
SyncLoopHook源码大致实现
class SyncLoopHook { constructor () { this.hook; } tap (name, fn) { this.hook = fn } call () { let result = this.hook(...arguments); // do{ // result = this.hook(...arguments) // } while(result) while(result) { result = this.hook(...arguments) } } } 复制代码
Async*类型的hooks
支持tap、tapPromise、tapAsync注册 每次都是调用tap、tapSync、tapPromise注册不同类型的插件钩子,通过调用call、callAsync 、promise方式调用。其实调用的时候为了按照一定的执行策略执行,调用compile方法快速编译出一个方法来执行这些插件。
异步并发执行
1、AsyncParallelHook
异步并发执行 复制代码
用法
有三种注册方式
// 第一种注册方式 tap myAsyncParallelHook.tap('1', function (name) { console.log(1, name) }) myAsyncParallelHook.tap('2', function (name) { console.log(2, name) }) myAsyncParallelHook.tap('3', function (name) { console.log(3, name) }) myAsyncParallelHook.callAsync('liu', function () { console.log('over') }); // 1 'liu' // 2 'liu' // 3 'liu' // over // 第二种注册方式 tapAsync 凡事有异步 必有回调 console.time('cost') myAsyncParallelHook.tapAsync('1', function (name, callback) { setTimeout(function () { console.log(1, name) callback() }, 1000) // callback() }) myAsyncParallelHook.tapAsync('2', function (name, callback) { setTimeout(function () { console.log(2, name) callback() }, 2000) // callback() }) myAsyncParallelHook.tapAsync('3', function (name, callback) { setTimeout(function () { console.log(3, name) callback() }, 3000) }) myAsyncParallelHook.callAsync('liu', () => { console.log('over') console.timeEnd('cost') }); 并行执行 花费的总时间是时间最长的那个 //1 'liu' //2 'liu' //3 'liu' //over //cost: 3005.083ms // 第三种注册方式 tapPromise console.time('cost') myAsyncParallelHook.tapPromise('1', function (name) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log(1, name) resolve() }, 1000) }) }) myAsyncParallelHook.tapPromise('2', function (name) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log(2, name) resolve() }, 2000) }) }) myAsyncParallelHook.tapPromise('3', function (name) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log(3, name) resolve() }, 3000) }) }) myAsyncParallelHook.promise('liu').then(function () { console.log('ok') console.timeEnd('cost') }, function () { console.log('error') console.timeEnd('cost') }); // 1 'liu' // 2 'liu' // 3 'liu' // ok // cost: 3001.903ms 复制代码
...
2、AsyncParallelBailHook
有一个失败了 其他的都不用走了 复制代码
用法
const { AsyncParallelBailHook } = require('tapable'); const myAsyncParallelBailHook = new AsyncParallelBailHook(['name']); // 第一种注册方式 tap myAsyncParallelBailHook.tap('1', function (name) { console.log(1, name) return 'wrong' }) myAsyncParallelBailHook.tap('2', function (name) { console.log(2, name) }) myAsyncParallelBailHook.tap('3', function (name) { console.log(3, name) }) myAsyncParallelBailHook.callAsync('liu', function () { console.log('over') }); // 1 'liu' // over // 第二种注册方式 tapAsync 凡事有异步 必有回调 console.time('cost') myAsyncParallelBailHook.tapAsync('1', function (name, callback) { setTimeout(function () { console.log(1, name) return 'wrong';// 最后的回调就不会调用了 callback() }, 1000) // callback() }) myAsyncParallelBailHook.tapAsync('2', function (name, callback) { setTimeout(function () { console.log(2, name) callback() }, 2000) // callback() }) myAsyncParallelBailHook.tapAsync('3', function (name, callback) { setTimeout(function () { console.log(3, name) callback() }, 3000) }) myAsyncParallelBailHook.callAsync('liu', () => { console.log('over') console.timeEnd('cost') }); // 1 'liu' // 2 'liu' // 3 'liu' // 第三种注册方式 tapPromise console.time('cost') myAsyncParallelBailHook.tapPromise('1', function (name) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log(1, name) reject('wrong');// reject()的参数是一个不为null的参数时,最后的回调就不会再调用了 }, 1000) }) }) myAsyncParallelBailHook.tapPromise('2', function (name) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log(2, name) resolve() }, 2000) }) }) myAsyncParallelBailHook.tapPromise('3', function (name) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log(3, name) resolve() }, 3000) }) }) myAsyncParallelBailHook.promise('liu').then(function () { console.log('ok') console.timeEnd('cost') }, function () { console.log('error') console.timeEnd('cost') }); // 1 'liu' // error // cost: 1006.030ms // 2 'liu' // 3 'liu' 复制代码
异步串行执行
1、AsyncSeriesHook
用法
let { AsyncSeriesHook } = require('tapable'); let myAsyncSeriesHook = new AsyncSeriesHook(['name']); console.time('coast') myAsyncSeriesHook.tapAsync('1', function (name, cb) { setTimeout(function () { console.log('1', name) cb() }, 1000) }); myAsyncSeriesHook.tapAsync('2', function (name, cb) { setTimeout(function () { console.log('2', name) cb() }, 2000) }); myAsyncSeriesHook.tapAsync('3', function (name, cb) { setTimeout(function () { console.log('3', name) cb() }, 3000) }); myAsyncSeriesHook.callAsync('liu', function () { console.log('over') console.timeEnd('coast') }) // 1 liu // 2 liu // 3 liu // over // coast: 6010.515ms 异步串行执行消耗的时间是所有的总和 复制代码
AsyncSeriesHook源码大致实现
class AsyncSeriesHook{ constructor() { this.hooks = []; } tapAsync () { this.hooks.push(arguments[arguments.length-1]); } callAsync () { let args = Array.from(arguments); //将传进来的参数转化为数组 let done = args.pop(); // 取出数组的最后一项 即成功后的回调函数 let index = 0; let that = this; function next(err) { if (err) return done(); let fn = that.hooks[index++]; fn ? fn(...args, next) : done(); } next() } } 复制代码
2、AsyncSeriesBailHook
用法
// 异步串行执行 let { AsyncSeriesBailHook } = require('tapable'); let myAsyncSeriesBailHook = new AsyncSeriesBailHook(['name']); console.time('coast') myAsyncSeriesBailHook.tapAsync('1', function (name, cb) { setTimeout(function () { console.log('1', name) cb('wrong') }, 1000) }); myAsyncSeriesBailHook.tapAsync('2', function (name, cb) { setTimeout(function () { console.log('2', name) cb() }, 2000) }); myAsyncSeriesBailHook.tapAsync('3', function (name, cb) { setTimeout(function () { console.log('3', name) cb() }, 3000) }); myAsyncSeriesBailHook.callAsync('liu', function () { console.log('over') console.timeEnd('coast') }) // 1 liu // over // coast: 1004.175ms 复制代码
AsyncSeriesBailHook 源码的大致实现
class AsyncSeriesBailHook{ constructor() { this.hooks = []; } tapAsync () { this.hooks.push(arguments[arguments.length-1]); } callAsync () { let args = Array.from(arguments); //将传进来的参数转化为数组 let done = args.pop(); // 取出数组的最后一项 即成功后的回调函数 let index = 0; let that = this; function next(err) { if (err) return done(); let fn = that.hooks[index++]; fn ? fn(...args, next) : done(); } next() } } 复制代码
3、AsyncSeriesWaterfallHook
下一个任务要拿到上一个任务的返回值 复制代码
用法
// 异步串行执行 let { AsyncSeriesWaterfallHook } = require('tapable'); let myAsyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook(['name']); console.time('coast') myAsyncSeriesWaterfallHook.tapAsync('1', function (name, cb) { setTimeout(function () { console.log('1', name) cb(null, 'aa') }, 1000) }); myAsyncSeriesWaterfallHook.tapAsync('2', function (name, cb) { setTimeout(function () { console.log('2', name) cb(null, 'bb') }, 2000) }); myAsyncSeriesWaterfallHook.tapAsync('3', function (name, cb) { setTimeout(function () { console.log('3', name) cb(null, 'cc') }, 3000) }); myAsyncSeriesWaterfallHook.callAsync('liu', function () { console.log('over') console.timeEnd('coast') }) // 1 liu // 2 aa // 3 bb // over // coast: 6011.774ms 复制代码
AsyncSeriesWaterfallHook 源码的大致实现
class AsyncSeriesWaterfallHook{ constructor() { this.hooks = []; } tapAsync () { this.hooks.push(arguments[arguments.length-1]); } callAsync () { let args = Array.from(arguments); //将传进来的参数转化为数组 let done = args.pop(); // 取出数组的最后一项 即成功后的回调函数 let index = 0; let that = this; function next(err, data) { if(index>=that.hooks.length) return done(); if (err) return done(err); let fn = that.hooks[index++]; if (index == 1) { fn(...args, next) } else { fn(data, next) } } next() } } 复制代码
以上所述就是小编给大家介绍的《webpack 4.0 Tapable 类中的常用钩子函数源码分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
How to Design Programs, 2nd Edition
Matthias Felleisen、Robert Bruce Findler、Matthew Flatt、Shriram Krishnamurthi / MIT Press / 2018-5-4 / USD 57.00
A completely revised edition, offering new design recipes for interactive programs and support for images as plain values, testing, event-driven programming, and even distributed programming. This ......一起来看看 《How to Design Programs, 2nd Edition》 这本书的介绍吧!