我的模块加载系统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) }); }
没有空间,恕不演示了!
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年