webpack源码的精妙方法
花开花落花中妖 人气:0前言
过年这一段时间一直在研究webpack的源码,由于过年周围气氛比较欢快,心态有点飘导致没有沉下心来仔细研究其中的细节。经过反思之后,静心重新捋顺webpack的源码,这时发现不少巧妙的方法值得学习。这里我已经迫不及待的跟大家分享了,希望对大家平常开发过程中有所帮助。
精妙方法
缓存函数
这个方法最精妙的地方在于将执行结果缓存,减少函数的重复执行以达到提升性能的目的,对于执行越复杂越耗时的函数收益越大。但是,不适用于动态执行结果的函数。
const memoize = fn => { let cache = false; let result = undefined; return () => { if (cache) { return result; } else { result = fn(); cache = true; fn = undefined; return result; } }; };
这个方法跟惰性函数有点相似,只在函数第一次调用的时候执行,将fn()的执行结果缓存到result上,然后通过cache设置为true来标记缓存已开启。这里还有个细节值得学习:由于闭包的原因,fn方法被新的函数持有,一直在调用栈中得不到释放,而在代码中有一句fn = undefined,手动释放内存。
属性劫持
这个方法通过自定义get方法或者value值来重定义obj的属性。实现的结果有点类似于Object.freeze,但又不完全相同。通过Object.defineProperty定义name的get属性描述符来保证obj的值永远不变;通过Object.defineProperty来将name的writable设置为false来保证obj的value不会被改变。
const mergeExports = (obj, exports) => { ... for (const name of Object.keys(descriptors)) { const descriptor = descriptors[name]; if (descriptor.get) { const fn = descriptor.get; Object.defineProperty(obj, name, { configurable: false, enumerable: true, get: memoize(fn) }); } else if (typeof descriptor.value === "object") { Object.defineProperty(obj, name, { configurable: false, enumerable: true, writable: false, value: mergeExports({}, descriptor.value) }); } else { ... } } return Object.freeze(obj); };
这里还有个小的知识点:通过将属性的configurable属性描述符设置为false来保证属性不可被删除。
数组比较
进行两个数组是否相等的比较。想必大家都知道,如果使用==进行数组比较的话,是进行引用地址的比较,所以想要判断两个数组是没有方法进行直接比较的。而webpack源码中的这个方法给我提供了一种数组比较的方式。当然了,这个方法只适用于扁平化的一维基本类型数组,如果想要比较复杂的情况的话,需要在for循环的基础上稍加改造。
exports.equals = (a, b) => { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; };
配置项校验
webpack中的实现的代码量比较多,我们就只说一下大体实现思路,通过定义需要校验的所有配置项的Schema校验规则。大体Schema格式如下:
"Amd": { "description": "Set the value of `require.amd` and `define.amd`. Or disable AMD support.", "anyOf": [ { "description": "You can pass `false` to disable AMD support.", "enum": [false] }, { "description": "You can pass an object to set the value of `require.amd` and `define.amd`.", "type": "object" } ] },
其中key对应的是需要校验的配置项,value中的desciption对应的是提示信息,其余部分对应的是校验规则。
结尾
加载全部内容