每天读一点webpack-001
每天读一点webpack
day-01-webpack入口
- npm包我们可以通过
package.json
中的包信息文件快速的定位到这个包的入口文件,通过webpack
的包信息main
字段可以得到整个webpack
项目的入口位置为lib/index.js
入口文件
- 入口文件中通过动态导入的方式将项目的各个模块进行整合,如各种内部插件,内部依赖,内部配置信息等。
包的缓存
在入口文件中通过一个缓存方法
lazyFunction
将webpack
这个模块进行了缓存操作,其中源码如下// 通过 lazyFunction 对指定包进行缓存操作 const fn = lazyFunction(() => require("./webpack"));
如何实现包的缓存?
lazyFunction
其实是使用了闭包的方式对包进行了缓存。先看关键源码,memoize
方法通过闭包的方式将入参中函数fn
的执行结果缓存起来,这样在下次想要获取fn
函数的返回结果时就无需在执行fn
方法了。const memoize = fn => { let cache = false let result = undefined // 返回一个函数 构造一个 闭包,用于缓存 fn 函数的返回值 return () => { if(cache) { return result }else { result = fn() cache = true return result } } }
而
lazyFunction
只是对以上memoize
方法的简单封装,使得其返回的方法支持参数传递,实际上相当于一次柯里化处理。但是memoize
中并没有接受其传入的参数,因此此次封装并无效果。其实可以直接将memoize
方法进行一步改造,让其作为返回值的函数接受入参,并传递给fn
函数即可达到预期效果,const lazyFunction = factory => { const fac = memoize(factory); const f = (...args) => { return fac()(...args); } return f; };
包的合并
入口文件中并没有简单对依赖进行组合,而是通过
mergeExports
方法对各个模块进行了组装。该方法主要通过Object.defineProperty
来达到对象组装的目的,这样做的目的是,可以更好的对对象的属性进行控制,如属性的读、写、删除、枚举。相关源码如下getOwnPropertyDescriptors
用于获取对象上 所有属性的描述符该方法中使用了递归的方式将 对象类型的属性平铺开来,这样使得 源码模块特间关系可以保持的同时,降低了模块功能应用的复杂性。
const mergeExports = (obj, exports) => { const descriptors = Object.getOwnPropertyDescriptors(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 { throw new Error( "Exposed values must be either a getter or an nested object" ); } } return /** @type {A & B} */ (Object.freeze(obj)); };
转载自:https://segmentfault.com/a/1190000040790686