我的模块加载系统v2
可以一句概括为利用动态script实现异步回调,每个模块位于独立的文件中,自行处理依赖。
//核心模块,包含加载逻辑,最下面的query模块其实也没有必要用use方法包含起来,单纯让逻辑更清晰些。
//总之,除了加载逻辑外,其他模块都写在use方法的回调函数中。模块与一般的回调函数的区别是,模块只能
//执行一次(因为没有必要重复执行),因此我们要在其里面使用arguments.callee._attached = true标识它
//另,对于文件的重复加载对策是使用一个hash来存在这些已加载的模块,这个由loaded方法来处理
var dom = window.dom = {
genScriptNode : function() {
var script = document.createElement("script");
script.type = "text/javascript";
script.charset = "utf-8";
return script;
},
head:function(){
return document.getElementsByTagName("head")[0]
},
env:{
loaded:{}, //已经加载的文件hash
queue:[] //待处理列队
},
//使用模块
use: function(name, callback, config) {
config = config || {};
var depends = config.use || [],queue = dom.env.queue,i,item;
//去掉“dom.”前缀,防止混乱
name = name.indexOf("dom.") === 0 ? name.slice(4):name
if(!name)//必须指定模块名
return this;
if(queue.length){//配置列队
//如果是加载一个模块过程中需要加载其他模块,我们需要首先执行那些依赖模块,
//因此顺序都是倒着加入队列
queue.unshift(callback);
queue.unshift(name);
i = depends.length;
while (i--) {
//添加所有依赖
queue.unshift(depends[i]);
}
}else{//配置列队
for(i=0;item = depends[i++];){
//添加所有依赖
queue.push(item);
}
//添加模块名,用于可能的文件加载
queue.push(name);
//添加回调函数(也有可能是模块本身)
queue.push(callback);
}
dom._loadNext();//开始执行列队
return this;
},
//用于标识此模块所在的JS文件已经被加载,并清除timeoutID
loaded:function(name){
dom.env.loaded[name] = true;
var timeoutID = dom.namespace("dom."+name,true).timeoutID;
timeoutID && clearTimeout(timeoutID)
},
//name为模块名,create不存在此属性是否创建一个空对象
namespace:function(name,create,context){
var parts=name.split("."),obj = context || window;
for(var i=0, p; obj && (p=parts[i]); i++){
if(i == 0 && this[p]){
p = this[p];
}
obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined));
}
return obj;
},
//加载文件,装配新模块或执行回调
_loadNext:function(){
var env = dom.env, queue = env.queue,loaded = env.loaded;
if(queue.length){
var item = queue.shift();
if(typeof item === "string" ){
if(!loaded[item]){//如果此模块所在的JS文件还没有加载,则加载它
var module = "dom."+item,url;
//处理dom.node(http://www.cnblogs.com/rubylouvre/dom/node.js)的情形
var _u = module.match(/\(([^)]+)\)/);
url = _u && _u[1] ? _u[1] : dom.getBasePath()+"/"+ module.replace(/\./g, "/") + ".js";
var script = dom.genScriptNode();
script.src = url
dom.head().appendChild(script);
var scope = dom.namespace(module,true)
scope.timeoutID = setTimeout(function(){
alert("Fail to load module '"+module+"'!")
},1000)
//让将要加载JS文件中的函数调用 dom._loadNext
}else{
//否则跳过,继续处理下一个
dom._loadNext();
}
//如果是函数,可能是普通的回调函数,也可能是模块本身
}else if(typeof item === "function"){
if(!item._attached){//如果是模块则只会执行一次
item();
}
dom._loadNext();
}
}
},
//获取核心模块所在的JS文件所在的文件夹路径
getBasePath : function(){
var result = "",m;
try{
a.b.c();
}catch(e){
if(e.fileName){//firefox
result = e.fileName;
}else if(e.sourceURL){//safari
result = e.sourceURL;
}else if(e.stacktrace){//opera9
m = e.stacktrace.match(/\(\) in\s+(.*?\:\/\/\S+)/m);
if (m && m[1])
result = m[1]
}else if(e.stack){//chrome 4+
m= e.stack.match(/\(([^)]+)\)/)
if (m && m[1])
result = m[1]
}
}
if(!result){//IE与chrome4- opera10+
var scripts = document.getElementsByTagName("script");
var reg = /dom([.-]\d)*\.js(\W|$)/i,src
for(var i=0, el; el = scripts[i++];){
src = !!document.querySelector ? el.src:
el.getAttribute("src",4);
if(src && reg.test(src)){
result = src
break;
}
}
}
return result.substr( 0, result.lastIndexOf('/'));
}
};
dom.loaded("query") ;
dom.use("query",function(){//☆☆☆这里不会使用动态标签来加载它,因为它已调用了loaded方法,它只会顺势执行回调函数
arguments.callee._attached = true;
alert("query加载成功")
},{
use:["behaviour"]
});
node模块
//位于/dom/node.js
dom.loaded("node");
dom.use("node",function(){
arguments.callee._attached = true;
dom.node1 ="开始装载node模块"
});
event模块
//位于/dom/event.js
dom.loaded("event");
dom.use("event",function(){
arguments.callee._attached = true;
dom.event1 = "ddd"
alert("event模块已装载成功")
})
behaviour模块
//位于/dom/behaviour.js
dom.loaded("behaviour");
dom.use("behaviour",function(){
arguments.callee._attached = true;
alert("behaviour模块已装载成功")
})
前台调用
window.onload=function(){
dom.use("event", function(){//★★★这里会使用动态标签,加载JS文件,装载event模块与执行回调函数
alert(dom.event1)
});
}
没有空间,恕不演示了!
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年
浙公网安备 33010602011771号