异步模块模式

异步模块模式AMD是当请求发出后,继续其他业务逻辑,直到模块加载完成执行后续逻辑,实现模块开发中的对模块加载完成后的引用,大名鼎鼎的require.js就是以它为思想的,异步模块模式不属于一般定义的23种设计模式的范畴,而通常将其看作广义上的架构型设计模式。

描述

异步模块模式主要是用在浏览器环境中,浏览器环境不同于服务器环境,为了不阻塞渲染线程通常以异步的方式来加载外部Js文件,因此要使用文件中的某些模块方法必须要经历文件加载过程,而对于这种问题同步模块模式则无法适用,需要使用异步模块模式。异步模块模式不仅减少了多人开发过程中变量、方法名被覆盖的问题,而且增加了模块依赖,使开发者不必担心某些方法尚未加载或未加载完成造成的无法使用问题,异步加载部分功能也可以将更多首屏不必要的功能剥离出去,减少首屏加载成本。

实现

// dom.js
F.module("./dom", function() {
    return {
        g: function(id) {
            return document.getElementById(id);
        },
        html: function(id, html) {
            if (!html) return this.g(id).innerHTML;
            else this.g(id).innerHTML = html;
        }
    }
});
// event.js
F.module("./event", ["./dom"], function(dom) {
    return {
        on: function(id, type, fn) {
            dom.g(id)["on" + type] = fn;
        }
    }
});
<!-- demo.html -->
<!DOCTYPE html>
<html>

<head>
    <title>异步模块模式</title>
</head>

<body>
    <div id="demo">Click Me</div>
</body>
<script type="text/javascript">
(function(F) {
    const moduleCache = {};

    function getUrl(moduleName) {
        return String(moduleName).replace(/\.js$/g, "") + ".js"
    }

    function loadScript(src) {
        let _script = document.createElement("script");
        _script.type = "text/javascript";
        _script.charset = "UTF-8";
        _script.async = true;
        _script.src = src;
        document.body.appendChild(_script);
    }

    function setModule(moduleName, params, callback) {
        let _module = null, fn = null;
        if(moduleCache[moduleName]) {
            _module = moduleCache[moduleName];
            _module.status = "loaded";
            _module.exports = callback ? callback.apply(_module, params) : null;
            while (fn = _module.onload.shift()) {
                fn(_module.exports)
            }
        } else {
            callback && callback.apply(null, params);
        }
    }

    function loadModule(moduleName, callback) {
        let _module = "";
        if (moduleCache[moduleName]) {
            _module = moduleCache[moduleName];
            if (_module.status === "loaded") {
                // 这个很重要,loadModule一定是异步的,effectiveJS 上的某一条建议有写,永远不要同步的调用异步函数,这非常重要
                setTimeout(callback(_module.exports), 0);
            } else {
                // 加载完成的时候调用
                _module.onload.push(callback);
            }
        } else {
            // 第一次加载
            moduleCache[moduleName] = {
                moduleName: moduleName,
                status: "loading",
                exports: null,
                onload: [callback]
            };
            loadScript(getUrl(moduleName));
        }
    }

    F.module = function(...args) {
        // 获取模块构造函数(参数数组中最后一个参数成员)
        let callback = args.pop();
        // 获取依赖模块(紧邻回调函数参数,且数据类型为数组)
        let deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [];
        // 该模块url(模块ID)
        let url = args.length ? args.pop() : null;
        //  依赖模块序列
        let params = [];
        // 未加载的依赖模块数量统计
        let depsCount = 0;

        if(deps.length) {
            deps.forEach((v ,i) => {
                // 增加未加载依赖模块数量统计
                depsCount++;
                // 异步加载依赖模块
                loadModule(deps[i], function(mod) {
                    // 依赖模块序列中添加依赖模块数量统一减一
                    depsCount--;
                    params[i] = mod;
                    // 如果依赖模块全部加载
                    if(depsCount === 0) {
                        // 在模块缓存器中矫正该模块,并执行构造函数
                        setModule(url, params, callback);
                    }
                });
            })     
        } else { // 无依赖模块,直接执行回调函数
            // 在模块缓存器中矫正该模块,并执行构造函数
            setModule(url, [], callback);
        }
    }

})((() => window.F = ({}))());


F.module(["./event", "./dom"], function(events, dom) {
    console.log(events, dom)
    events.on("demo", "click", function() {
        dom.html("demo", "success");
    })
});
</script>

</html>

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://www.jianshu.com/p/d71fc902051e https://blog.csdn.net/WuLex/article/details/107350493 https://blog.csdn.net/a545415/article/details/77930534