异步模块模式(AMD):请求发出后,继续其他业务逻辑,直到模块加载完成后执行后续逻辑,实现模块开发对模块加载完成后的引用
由于同步模块会立即引用模块,如果改模块在另一个js,而该js正在加载,无法立即得到该模块,就会出问题
//闭包环境
//向闭包传入模块管理器对象F,~屏蔽压缩文件时,前面漏写;报错
~(function(F){
var moduleCache = {};
})((function(){
//创建模块管理器对象F,并保存在全局作用域中
return window.F = {};
})());
//创建与调度模块
//这里的module方法和同步模式中不太一样,集创建和调度于一身
//它会遍历所有模块,直到都存在才会运行回调,否则加载相应文件,直到文件加载完才执行回调
F.module = function(url, modDeps, modCallback){ //模块url,依赖模块,模块主函数
var args = [].slice.call(arguments), //将参数转为数组
callback = args.pop(), //获取模块构造函数
//获取依赖模块
deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [],
url = args.length ? args.pop() : null, //模块url(模块ID)
params = [], //依赖模块序列
depsCount = 0, //未加载的依赖模块数量统计
i = 0, //依赖模块序列中的索引值
len; //依赖模块长度
//获取依赖模块长度
if(len = deps.length){
while(i < len){ //遍历依赖模块
(function(i){ //闭包保存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 = {};
//加载依赖模块对应文件并执行回调函数
//有三种情况
//1.文件已被加载过,区分已经加载完成还是正在加载,如果已经加载完成则调用
//2.未加载完成,将加载完成的回调缓存进模块加载完成的回调函数容器中(onload数组容器)
//3.文件未被加载,则加载该文件并将依赖模块的初始化信息写入模块缓存器
var loadModule = function(moduleName, callback){ //模块路径(id),回调
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){
return String(moduleName).replace(/\.js$/g, '') + '.js';
};
//加载脚本文件
var loadScript = function(src){
var _script = document.createElement('script');
_script.type = 'text/JavaScript';
_script.charsrt = 'UTF-8';
_script.async = true;
_script.src = src;
document.getElementsByTagName('head')[0].appendChild(_script);
};
//执行模块回调函数
//对创建的模块,如果所有依赖模块加载完,则执行
//对被依赖的模块,所在文件加载后执行该依赖模块又间接地使用该方法
//对匿名模块(F.module方法中无url参数),执行过程也会使用该方法
var setModule = function(moduleName, params, callback){ //模块id,依赖模块,模块构造函数
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(null, params);
}
};
//使用
//模块dom
F.module('lib/dom', function(){ console.log(1) });
//模块event,依赖模块dom
F.module('lib/event', ['lib/dom'], function(dom){ console.log(2) })
//在html引用模块
F.module(['lib/event', 'lib/dom'], function(event, dom){ console.log(3) })
林秀栋的技术博客