两个鬼故事乘胜追击百度云梦到被蛇咬是什么意思珠海起名柳传志语录大灌篮高清下载韩国经典三级属鼠的男孩子起名大全好听的博客名建筑物起名微软平板破事儿百度影音门店取名起名大全测试零食什么牌子起名验房程序保尔柯察金易名网站免费起名免费起名网有哪些网站二字公司起名大全给女猫咪起名字柳树的描写优美句子海底两万里读书笔记btv体育燕起名姓王有起名大全p2p理财公司排名徽商银行个人网上银行店起名美甲电信积分2020起名字公司女装品牌起名字好听少年生前被连续抽血16次?多部门介入两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”淀粉肠小王子日销售额涨超10倍高中生被打伤下体休学 邯郸通报单亲妈妈陷入热恋 14岁儿子报警何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言张家界的山上“长”满了韩国人?男孩8年未见母亲被告知被遗忘中国拥有亿元资产的家庭达13.3万户19岁小伙救下5人后溺亡 多方发声315晚会后胖东来又人满为患了张立群任西安交通大学校长“重生之我在北大当嫡校长”男子被猫抓伤后确诊“猫抓病”测试车高速逃费 小米:已补缴周杰伦一审败诉网易网友洛杉矶偶遇贾玲今日春分倪萍分享减重40斤方法七年后宇文玥被薅头发捞上岸许家印被限制高消费萧美琴窜访捷克 外交部回应联合利华开始重组专访95后高颜值猪保姆胖东来员工每周单休无小长假男子被流浪猫绊倒 投喂者赔24万小米汽车超级工厂正式揭幕黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发当地回应沈阳致3死车祸车主疑毒驾恒大被罚41.75亿到底怎么缴妈妈回应孩子在校撞护栏坠楼外国人感慨凌晨的中国很安全杨倩无缘巴黎奥运校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变王树国卸任西安交大校长 师生送别手机成瘾是影响睡眠质量重要因素国产伟哥去年销售近13亿阿根廷将发行1万与2万面值的纸币兔狲“狲大娘”因病死亡遭遇山火的松茸之乡“开封王婆”爆火:促成四五十对奥巴马现身唐宁街 黑色着装引猜测考生莫言也上北大硕士复试名单了德国打算提及普京时仅用姓名天水麻辣烫把捣辣椒大爷累坏了
完整的手写源码仓库
目录
tapable hook
的源码,每个hook
都用自己的思路实现一遍,并且和官方的hook
执行时间做个对比。tapable的设计理念:单态、多态及内联缓存
由于在
webpack
打包构建的过程中,会有上千(数量其实是取决于自身业务复杂度)个插件钩子执行,同时同类型的钩子在执行时,函数参数固定,函数体相同,因此tapable
针对这些业务场景进行了相应的优化。这其中最重要的是运用了 单态性及多态性概念, 内联缓存的原理,也可以看这个 issue。为了达到这个目标,tapable
采用new Function
动态生成函数执行体的方式,主要逻辑在源码的 HookCodeFactory.js文件中。如何理解 tapable 的设计理念
思考下面两种实现方法,哪一种执行效率高,哪一种实现方式简洁?
callFn
及callFn2
的目的都是为了实现将一组方法以相同的参数调用,依次执行。很显然,方法一效率明显更高,并且容易扩展,能支持传入数量不固定的一组方法。但是,如果根据 单态性以及内联缓存的说法,很明显方法二的执行效率更高,同时也存在一个问题,即只支持传入a,b,c三个方法,参数形态也固定,这种方式显然没有方法一灵活,那能不能同时兼顾效率以及灵活性呢?答案是可以的。我们可以借助new Function
动态生成函数体的方式。当我们在浏览器控制台执行上述代码时:
拼接后的完整函数执行体:
可以看到,通过这种动态生成函数执行体的方式,我们能够同时兼顾性能及灵活性。我们可以通过
tap
方法添加任意数量的任务,同时通过在初始化构造函数时new HookCodeFactory(['x', 'y', ..., 'n'])
传入任意参数。实际上,这正是官方
tapable
的 HookCodeFactory.js的简化版本。这是tapable
的精华所在。tapable源码解读
tapable
最主要的源码在Hook.js
以及HookCodeFactory.js
中。Hook.js
主要是提供了tap
、tapAsync
、tapPromise
等方法,每个Hook
都在构造函数内部调用const hook = new Hook()
初始化hook
实例。HookCodeFactory.js
主要是根据new Function
动态生成函数执行体。demo
以
SyncHook.js
为例,SyncHook
钩子使用如下:我们用这个demo做为用例,一步步debug。
SyncHook.js
源码主要逻辑如下:
Hook.js
源码主要逻辑如下:
思考Hook.js源码中的几个问题
this.compile
、this.tap
、this.tapAsync
以及this.tapPromise
等方法当我们每次调用
testhook.tap
方法注册插件时,流程如下:方法往
this.taps
数组中添加一个插件。this.__insert
方法逻辑比较简单,但这里有一个细节需要注意一下,为什么每次注册插件时,都需要调用this._resetCompilation()重置this.call等方法? 我们稍后再看下这个问题。先继续debug。当我们 第一次(注意是第一次) 调用
testhook.call
时,实际上调用的是CALL_DELEGATE
方法CALL_DELEGATE
调用this._createCall
函数根据注册的this.taps
动态生成函数执行体。并且this.call
被设置成this._createCall
的返回值缓存起来,如果this.taps
改变了,则需要重新生成。此时如果我们第二次调用
testhook.call
时,就不需要再重新动态生成一遍函数执行体。这也是tapable的优化技巧之一。这也回答了 问题一:为什么需要 CALL_DELEGATE。如果我们调用了n次
testhook.call
,然后又调用testhook.tap
注册插件,此时this.call
已经不能重用了,需要再根据CALL_DELEGATE
重新生成一次函数执行体,这也回答了问题二:为什么每次调用 testhook.tap() 注册插件时,都需要重置this.call等方法。可想而知重新生成的过程是很耗时的。因此我们在使用tapable
时,最好一次性注册完所有插件,再调用call
避免下面的调用方式:
现在让我们看看第三个问题,调用
this.compile
方法时,实际上会调用HookCodeFacotry.js
中的setup
方法:对于问题四,实际上这和 V8 引擎的
Hidden Class
有关,通过在构造函数中绑定这些方法,类中的属性形态固定,这样在查找这些方法时就能利用 V8 引擎中Hidden Class
属性查找机制,提高性能。HookCodeFactory.js
主要逻辑:
手写 tapable 每个 Hook
手写 tapable中所有的 hook,并比较我们自己实现的 hook 和官方的执行时间
这里面每个文件都会实现一遍 官方的 hook,并比较执行时间,以 SyncHook 为例,批量注册1000个插件时,我们自己手写的 MySyncHook执行时间0.12ms,而官方的需要6ms,这中间整整50倍的差距!!!
具体可以看 我的仓库
The text was updated successfully, but these errors were encountered: