JS 按需加载

2012-03-07 16:37:49

[code] /** * JS文件按需加载 * Cr&#101;ated:kyomic@163.com * * * --------Event:(complete, status, levelcomplete) * 1.complete 所有的JS请求结束, 事件参数:{} * 2.status 每一个JS请求状态发生改变, 事件参数:{ id:id, level: 优先级, src:文件路径, status:文件加载状态 } * 3.levelcomplete 一个level级的JS请求结束, 事件参数: { level: 优先级 } * * --------Method----------- * * Function Import(arr, folder, level); 载入Javascript文件 * @param arr:JS文件列表 * @param folder:本地文件相对目录 * @param level:加载优先级,0为最高级 * * Function isLoaded(level); 判断某一级别的JS是否下载完 * @param level:级别,如果为空,则判断所有级别 * * Function delay(timeout); 延时开始执行JS的加载过程, 单位ms * * Example: $SCOPE.addListener(&#34;complete&#34;, function(a){ Log.log(&#34;all finished.&#34;+this); }); $SCOPE.addListener(&#34;status&#34;, function(obj){ Log.log(&#34;onChange:&#34;+obj); }); $SCOPE.addListener(&#34;levelcomplete&#34;, function(obj){ Log.log(&#34;@@@levelcomplete:&#34;+obj.level); Log.log(&#34;isLevel:&#34;+obj.level+&#34;,loaded:&#34; + this.isLoaded( obj.level )); }); $SCOPE.import([ &#34;ui.js&#34;,&#34;ui.js&#34;], &#34;applib&#34;, 0); $SCOPE.import([ &#34;A_2.js&#34;,&#34;B_2.js&#34;], &#34;applib&#34;, 1); * * */ if(typeof Log==&#34;undefined&#34;){ Log = { log:function(){ if(window.console &amp;&amp; window.console.log){ window.console.log.apply(null, arguments); }else { try{ window.alert.apply(null, arguments); }catch(e){}; } } } } (function() { var TIMEOUT = 5000; var S_READY = 0; //等待加载 var S_LOADING = 2; //正在加载中 var S_COMPLETE = 1; //加载成功 var S_TIMEOUT = 3; //加载超时 var S_ERROR = 4; //加载失败 var S_ABORT = 5; //用户取消 var that = {}; var evt = {}; var head = null; //DOM head var minLevel = -1; //列表中的最小级别 var level = Number.MAX_VALUE; //当前正在加载JS级别 var startLoadTimeoutId; //startLoad会延时执行 var startLoadTimeout = 1; /** * cache format: * { id: 唯一标识 level: 优先级 ,0为最高 src:文件路径 status:文件加载状态 0:等待加载, 1:加载成功, 2: 正在加载中, 3:加载超时 4:加载失败 * */ var cache = {}; var loadList = []; // 正在加载中的队列 var loadedList = []; // 加载完成的列表(包括成功和失败的文件) function $each(obj, fn, bind) { for(var key in obj) { if(obj.hasOwnProperty(key)) { fn.call(bind, obj[key], key, obj); } } } /** * 将数组以属性值大小进行排序 * * arr:目标数组 * att:属性名 * desc:降序排列 */ function sortOn(arr, att, desc){ for(var i=0;i<arr.length;i++){ for(var j=i+1;j<arr.length;j++){ var tmp; if(arr[i][att]>arr[j][att]){ if(!desc){ tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp; }else{ tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } } } return arr; } function cr&#101;ateXMLHttp(){ var xmlHttp; if (window.ActiveXObject) { try { xmlHttp = new ActiveXObject(&#34;Microsoft.XMLHTTP&#34;); } catch (e1) { xmlHttp = new ActiveXObject(&#34;Msxml2.XMLHTTP&#34;); } } else if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } if (!xmlHttp) { throw new Error(&#34;Error:Ajax Cant&#39;t cr&#101;ate XMLHttp Object!&#34;); } return xmlHttp; } /** * 加载JS数组 * @param obj:Object JS对象 * @param crossdomain:Boolean 是否允许跨域加载 */ function loadJS( obj , crossdomain ) { //crossdomain = true; if(!head){ head = document.getElementsByTagName(&#34;head&#34;)[0]; } Log.log(&#34;loadJS...&#34;+obj.src+&#34;,lvl=&#34;+obj.level); obj.status = S_LOADING; if(!obj.__timeoutid){ clearTimeout(obj.__timeoutid); } var loader = obj.__loader; if(!loader){ if(crossdomain){ loader = document.cr&#101;ateElement(&#34;script&#34;); loader.type = &#34;text/javascript&#34;; loader.async = true; loader.setAttribute(&#34;script_id&#34;, obj.id); head.appendChild(loader); }else { loader = cr&#101;ateXMLHttp(); } obj.__close = function(){ if(this.__loader){ try{ this.__loader.onload = this.__loader.onreadystatechange = null; }catch(e){}; try{ this.__loader.abort(); }catch (e){}; this.__loader = null; //del&#101;te this.__close; //del&#101;te this.__timeout; //del&#101;te this.__timeoutid; //del&#101;te this.__loader; } if(this.__timeoutid){ clearTimeout(this.__timeoutid); } } obj.__loader = loader; } loader.onload = loader.onreadystatechange = function(arg) { //!this.readyState (firefox ,chrome only); var status = &#34;&#34;; try{ status = this.status; }catch (e){}; if(status == &#34;404&#34;) { if(obj.__close) obj.__close(); obj.status = S_ERROR; onStatusChange(obj); return; } if (!this.readyState || this.readyState == &#34;loaded&#34; || this.readyState == &#34;complete&#34;){ if(obj.__close) obj.__close(); Log.log(&#34;loaded&#34;); obj.status = S_COMPLETE; onStatusChange(obj); }else if(this.readyState == 4){ if(status == &#34;200&#34;){ var txt = loader.responseText; loader = document.cr&#101;ateElement(&#34;script&#34;); loader.type = &#34;text/javascript&#34;; loader.async = true; loader.setAttribute(&#34;script_id&#34;, obj.id); head.appendChild(loader); loader.text = txt; obj.status = S_COMPLETE; if(obj.__close) obj.__close(); onStatusChange(obj); } } } if(crossdomain){ loader.src = obj.src; }else{ loader.open(&#34;get&#34;, obj.src, true); loader.send(); } if(!obj.__timeout){ obj.__timeout = function(){ if(obj.__close) obj.__close(); obj.status = S_TIMEOUT; onStatusChange(obj); Log.log(&#34;timeout:&#34;+TIMEOUT); } } if(obj.__timeoutid){ clearTimeout(obj.__timeoutid); } obj.__timeoutid = setTimeout(obj.__timeout, TIMEOUT); } //加载完成/加载失败 function onStatusChange(obj){ loadedList.push(obj); trigger(&#34;status&#34;, obj); var idx = loadList.indexOf(obj); if(idx !=-1){ loadList.splice(idx,1); startLoad(); } } //开始加载 function startLoad(){ var arr = sortOn(loadList.concat(), &#34;level&#34;); Log.log(&#34;---------startloading-----------len:&#34;+ arr.length); var obj,i=0,j=0,len = arr.length; if(len>0) { if(arr[0].level != level &amp;&amp; level != Number.MAX_VALUE ) { trigger(&#34;levelcomplete&#34;, {level:level}); } level = arr[0].level; } if(minLevel <0 ) minLevel = level; for(var i=0;i<len;i++) { obj = arr[i]; //level = Math.min(Number(obj.level), level); if(Number(obj.level) <= level) { if(obj.status == S_READY){ loadJS(obj); } } } if(len == 0 &amp;&amp; loadedList.length!=0){ trigger(&#34;levelcomplete&#34;, {level:level}); trigger(&#34;complete&#34;); } } /** * * 检测JS是否下载成功 * @param lvl 可以指定所在级别的JS */ function isLoaded(lvl){ var loaded = true; var i=0, len = loadedList.length; if(typeof lvl == &#34;number&#34;){ for(i = 0; i < len; i ++ ){ if( loadedList[i].status != S_COMPLETE &amp;&amp; loadedList[i].level == lvl){ loaded = false; break; } } }else{ for(i = 0; i < len; i ++ ){ if( loadedList[i].status != S_COMPLETE ){ loaded = false; break; } } } return loaded; } function cancel(){ var i=0,len = cache.length; while(i<len){ if(cache[i].__close){ cache[i].__close();} if(cache[i].status == S_LOADING){ cache[i].status = S_ABORT; } i++; } } /** * * Function import * * @param arr: Array JS文件数组 * @param package:String 文件夹名,子文件夹用&#34;.&#34;分隔, 如 folder1.folder2, 对应目录 folder1/folder2 * @param level:Number JS文件加载优先级 0>1 */ function import(arr, package, level){ var path = &#34;&#34;; var id = &#34;&#34;; if(package &amp;&amp; package != &#34;root&#34;){ if(package.indexOf(&#34;http://&#34;)!=-1) { id = package; path = package; }else{ var a = package.split(&#34;.&#34;); if(a &amp;&amp; a.length>0){ path = a.join(&#34;/&#34;); } id = a.join(&#34;_&#34;); } }else{ id = &#34;root&#34;; } if(!level) level = 0; var uuid = &#34;&#34;; var obj; var i = 0; var len = arr.length; for(i=0;i<len;i++){ uuid = id + &#34;#&#34; + arr[i]; obj = cache[uuid]; if( !obj ) { obj = { &#34;id&#34; : uuid , &#34;level&#34;: level , &#34;src&#34; : path ? path + &#34;/&#34; + arr[i] : arr[i] , &#34;status&#34;: S_READY }; cache[uuid] = obj; if(obj.status == S_READY){ loadList.push(obj); } } } if(startLoadTimeoutId){ clearTimeout(startLoadTimeoutId); } startLoadTimeoutId = setTimeout(startLoad, startLoadTimeout); } function addListener(type, handler) { if(!evt[type]) evt[type] = []; evt[type].push(handler); } function removeListener (type, handler){ if(evt[type]){ var idx = evt[type].indexOf(handler); if(idx!=-1){ evt[type].splice(idx,1); } } } function trigger(type, data){ if(typeof data==&#34;undefined&#34;) data = {}; if(evt[type]){ for(var i=0;i<evt[type].length; i++){ evt[type][i].call(that, data); } } } function delay(timeout){ startLoadTimeout = timeout; } var spaceName = &#39;$SCOPE&#39;; that[&#34;import&#34;] = import; that[&#34;addListener&#34;] = addListener; that[&#34;removeListener&#34;] = removeListener; that[&#34;delay&#34;] = delay; that[&#34;isLoaded&#34;] = isLoaded; that[&#34;toString&#34;] = function(){ return &#34;[object $SCOPE ]&#34;;}; if(!this[spaceName]){ this[spaceName] = that; }else{ $each(that, function(prop, name){ if(!this[spaceName][name]){ this[spaceName][name] = prop; }else{ throw(&#39;Lib &#34;&#39; + spaceName + &#39;&#34; exists the same prop: &#39; + name); } }); that = this[spaceName]; } })(); [/code] 参考:http://hmking.blog.51cto.com/3135992/669507