webpack源码的精妙方法

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

webpack源码的精妙方法

花开花落花中妖   2022-05-23 我要评论

前言

过年这一段时间一直在研究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对应的是提示信息,其余部分对应的是校验规则。

结尾

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们