异步模块模式

异步模块模式

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

描述#

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

实现#

Copy
// 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; } } });
Copy
// event.js F.module("./event", ["./dom"], function(dom) { return { on: function(id, type, fn) { dom.g(id)["on" + type] = fn; } } });
Copy
<!-- 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>

每日一题#

Copy
https://github.com/WindrunnerMax/EveryDay

参考#

Copy
https://www.jianshu.com/p/d71fc902051e https://blog.csdn.net/WuLex/article/details/107350493 https://blog.csdn.net/a545415/article/details/77930534
posted @   WindRunnerMax  阅读(134)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示
CONTENTS