重新温习了下这段内容,发现各个浏览器的兼容性真的是搞大了头,处理起来很是麻烦。
现在现总结下并行加载多个js的方法:
1,对于动态createElement('script')的方式,对所有浏览器都是异步并行加载的。这里所说的并行不仅仅指的是
js并行加载,也包括js和其他资源比如图片,iframe的加载。但是此种方式在Firefox的2.0 3.0 3.1版本和opera 9.63
下是可以顺序执行的。但是由于Kyle的提议,现代浏览器都可以通过对动态创建的script元素设置属性async=false来使
js顺序执行。
2,可以通过document.write('<script>')的方式来并行加载(IE,现代浏览器)和顺序执行。
3,通过xhr加载js。但是有了同源的限制,因此对于外部js文件或者cdn上的js就无能为力。
已经有些大牛比如之前提到的Kyle已经提供了兼容个浏览器的标准库,项目名称是 LABjs。
自己写了一个简单的插件,目前并没有在IE6,7上测试。
if(!asyncHelper) var asyncHelper = {}; asyncHelper.cache = []; //存储获取到的js对象 asyncHelper.createAjax = (function(){ if('XMLHttpRequest' in window){ return function(){ return new XMLHttpRequest(); } }else{ var i= 0,len, fns = [function(){return new ActiveXObject('Microsoft.XMLHTTP')},function(){return new ActiveXObject('Msxml2.XMLHTTP')}, function(){return new ActiveXObject('Msxml2.XMLHTTP.3.0')},function(){return new ActiveXObject('Msxml2.XMLHTTP.6.0')}]; for(len = fns.length;i<len;i++){ try{ fns[i](); return fns[i]; break; }catch (e){ } } } })(); //功能函数,异步xhr加载js,并行无序加载js和其他资源,需要进行顺序控制;而且受同源限制, //无法使用cdn或外部引用js asyncHelper._loadJsWithXHR = function(url,fn,inOrder){ inOrder = inOrder || true; //默认顺序加载 var jsObj = {file: null,isLoaded:false,callback: fn},xhr, i,len; asyncHelper.cache.push(jsObj); xhr = this.createAjax(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ try{ if(xhr.status >=200 && xhr.status < 300 || xhr.status == 304){ jsObj.file = xhr.responseText; // 返回的js存入对象中 if(inOrder){ for(i=0,len=asyncHelper.cache.length;i<len;i++){ if(!asyncHelper.cache[i].file){ //避免重复解析已加载过得js文件 //从缓冲汇总删除已经加载的文件 if(i>0){ asyncHelper.cache.splice(0,i); } break; }else{ //Function相当于全局eval,不会改变作用域链 new Function(asyncHelper.cache[i].file)(); fn && fn(); //执行回调函数 if(i == len-1){ asyncHelper.cache = []; //清空缓存 } } } }else{ if(jsObj.file){ eval(jsObj.file); fn(); } } } }catch (loadError){ setTimeout(function(){ throw(new Error('loading with XHR response error--' + loadError)) },0); } } }; xhr.open('get',url); xhr.setRequestHeader('X-Request-With','XMLHttpRequest'); xhr.send(null); }; //通过创建script元素来异步加载js,支持跨域。在firefox,opera下也是顺序加载。 asyncHelper._loadJsWithDOMElement = function(url,fn){ var dom = document.createElement('script'); dom.type = 'application/javascript'; dom.async = false; dom.src = url; dom.isloaded = false; //执行回调函数,IE下使用onreadystatechange,w3c使用onload if('onload' in dom){ dom.onload = fn; }else{ dom.onreadystatechange = function(){ if((dom.readyState == 'loaded' || dom.readyState == 'complete') && !dom.isloaded){ fn(); dom.isloaded = true; } } } document.getElementsByTagName('head')[0].appendChild(dom); } //通过document.write插入script来进行并行加载脚本。gte IE8以及opera支持。 //全部浏览器支持此种方式的顺序加载js asyncHelper._loadJsWithScriptTag = function(url,fn){ document.writeln('<script type="application/javascript" src="' + url +'"><\/script>'); //给window绑定onload事件 if(window.addEventListener){ window.addEventListener('load',fn,false); }else{ window.attachEvent('onload',function(){ fn.call(this,window.event); }) } } //暴露外部接口,加载单个js文件 asyncHelper.loadScript = function(url,fn){ this._loadJsWithDOMElement(url,fn); } //加载多个js文件 asyncHelper.loadScripts = function(urls,fn){ function isSameDomain(url){ var domain = document.location.protocol + "//" + document.location.hostname + "/"; if(url.indexOf('http') !== -1 || url.indexOf('https') !== -1){ if(url.indexOf(domain) !== -1){ return true; } return false; } return true; } //如果url同源,则使用xhr加载 var i,len,flag,loadMethod; for(i=0,len=urls.length;i<len;i++){ if(flag = isSameDomain(urls[i])) continue; else break; } //默认xhr加载 loadMethod = asyncHelper._loadJsWithXHR; if(!flag){ //firefox opera使用DomElement方式加载,确保顺序性和异步加载 // 经测试,目前最新版本的Firefox亦不支持此特性。 //Firefox 4为了更向HTML5标准看齐,一度在开发者版本中去掉了对动态创建<script>来加载js文件的执行顺序支持: //<script> elements created using document.createElement() and inserted into a document now behave // according to the HTML5 specification by default. Scripts with the src attribute // execute as soon as available (without maintaining ordering) and scripts without // the src attribute execute synchronously. //Kyle向WebKit开发团队抗议,提了一个bug,最终得到了如他所愿的支持: //To make script-inserted scripts that have the src attribute execute in the insertion order, // set .async=false on them. if(navigator.userAgent.toLowerCase().indexOf('firefox') != -1 || navigator.userAgent.toLowerCase().indexOf('opera') != -1) loadMethod = asyncHelper._loadJsWithDOMElement; else loadMethod = asyncHelper._loadJsWithScriptTag; }for(i=0;i<len;i++){ if(i == len - 1){ loadMethod.call(asyncHelper,urls[i],fn); }else{ loadMethod.call(asyncHelper,urls[i]); } } } // 示例代码
asyncHelper.loadScripts(['http://libs.baidu.com/jquery/1.9.0/jquery.js','./a.js','./b.js'],function(){console.log('success')})