<body>
<h3>设计模式知识连载(46)---异步模块模式:</h3>
<div>
<p>
模块化:将复杂的系统分解成高内聚、低耦合的模块,使系统开发变得可控、可维护、可拓展,提高模块的复用率
</p>
<p>
请求发出后,继续其他业务逻辑,知道模块加载完成执行后续的逻辑,实现模块开发中对模块加载完成后的引用
</p>
</div>
<hr>
<script type="text/javascript">
/**
* 案例一:异步加载文件中的模块,方式一:初始
*/
// // 加载脚本文件
// var loadScript = function(src) {
// // 创建脚本元素
// var _script = document.createElement('script') ;
// // 设置类型
// _script.type = 'text/javascript' ;
// // 设置加载路径
// _script.src = src ;
// //将元素插入到页面中
// document.getElementsByTagName('head')[0].appendChild(_script) ;
// }
// // 加载localstorage文件
// loadScript('localstorage.js') ;
// // 使用localstorage模块
// F.module('localstorage', function(ls) {
// // do something
// }) ;
/**
* 案例一:异步加载文件中的模块,方式二:进阶
*/
/**
* 闭包环境
**/
// 向闭包中传入模块管理其对象F(~屏蔽雅素偶文件时,前面漏写;报错)
~(function(F) {
// 模块缓存器。存储已创建模块
var moduleCache = {} ;
}) ((function() {
// 创建模块管理器对象F,并保存在全局作用域中
return window.F = {} ;
}) ()) ;
/**
* 创建或调用模块方法
* @param url 参数为模块URL
* @param deps 参数为依赖模块
* @param callback 参数为模块主函数
**/
F.module = function(url, modDeps, modCallback) {
// 将参数转化为数组
var args = [].slice.call(arguments) ;
// 获取模块构造函数(参数数组中最后一个参数成员)
var callback = args.pop() ;
// 获取依赖模块(紧邻回调函数参数,且数据类型为数组)
var deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [] ;
// 该模块URL(模块ID)
var url = args.length ? args.pop() : null ;
// 依赖模块序列
var params = [] ;
// 未加载的依赖模块数量统计
var depsCount = 0 ;
// 依赖模块序列中索引值
var i = 0 ;
// 依赖模块序列长度
var len ;
// 获取依赖模块长度
if(len = deps.length) {
// 遍历依赖模块
while(i < len) {
// 闭包保存i
(function(i) {
// 增加未加载依赖模块数量统计
depsCount++ ;
// 异步加载依赖模块
loadModule(deps[i], function(mod) {
// 依赖模块序列中添加依赖模块接口引用
params[i] = mod ;
// 依赖模块加载完成,依赖模块数量统计减一
depsCount-- ;
// 如果依赖模块全部加载
if(depsCount === 0) {
// 在模块缓存器中矫正该模块,并执行构造函数
setModule(url, params, callback) ;
}
}) ;
}) (i) ;
// 遍历下一依赖模块
i++ ;
}
// 无依赖模块,直接执行回调函数
}else {
// 在模块缓存器中矫正该模块,并执行构造函数
setModule(url, [], callback) ;
}
}
// 加载模块
var moduleCache = {} ;
/***
* 异步加载依赖模块所在文件
* @param moduleName 模块路径(id)
* @param callback 模块加载完成回调函数
**/
var loadModule = function(moduleName, callback) {
// 依赖模块
var _module ;
// 如果依赖模块被要求加载过
if(moduleCache[moduleName] {
// 获取该模块信息
_module = moduleCache[moduleName] ;
// 如果模块加载完成
if(_module.status === 'loaded') {
// 执行模块加载完成回调函数
setTimeout(callback(_module.exports), 0) ;
}else {
// 缓存该模块所处文件加载完成回调函数
_module.onload.push(callback) ;
}
// 模块第一次被依赖引用
}else {
// 缓存该模块初始化信息
moduleCache[moduleName] = {
moduleName : moduleName, // 模块id
status : 'loading', // 模块对应文件加载状态(默认加载中)
exports : null, // 模块接口
onload : [callback] // 模块对应文件加载完成回调函数缓冲器
};
// 加载模块对应文件
loadScript(getUrl(moduleName)) ;
}
} ;
// 获取文件路径
var getUrl = function(moduleName) {
// 拼接完整的文件路径字符串,如'lib/ajas' => 'lib/ajax.js'
return String(moduleName).replace(/\.js$/g, '') + '.js' ;
} ;
// 加载脚本文件
var loadScript = function(src) {
var _script = document.createElement('script') ;
// 文本类型
_script.type = 'text/JavaScript' ;
// 确认编码
_script.charset = 'UTF-8' ;
// 异步加载
_script.async = true ;
// 文件路径
_script.src = src ;
// 插入页面中
document.getElementsByTagName('head')[0],appendChild(_script) ;
};
/***
* 设置模块并执行模块构造函数
* @param moduleName 模块id名称
* @param params 依赖模块
* @param callback 模块构造函数
**/
var setModule = function(moduleName, params, callback) {
// 模块容器,模块文件加载完成回调函数
var _module, fn ;
// 如果模块被调用过
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(nulll, params) ;
}
};
/******测试用例****************************************************/
// 在lib/dom.js中定义dom模块
F.module('lib/dom', function() {
return {
// 获取元素方法
g : function(id) {
return document.getElementById(id) ;
},
// 获取或者设置元素内容方法
html : function(id, html) {
if(html) {
this.g(id).innerHTML = html ;
}else {
return this.g(id).innerHTML ;
}
}
}
}) ;
// 在lib/event.js文件中定义event模块
F.module('lib/event', ['lib/dom'], function(dom) {
var events = {
// 绑定事件
on : function(id, type, fn) {
dom : g(id)['on' + type] = fn ;
}
} ;
return events
}) ;
// 在页面中引用dom模块与event模块,为页面中的button绑定事件,添加交互。【index.html页面】
F.module(['lib/event', 'lib/dom'], function(events, dom) {
events.on('demo', 'click', function() {
dom.html('demo', 'success') ;
}) ;
}) ;
</script>
</body>