前端工程师都知道script标签会阻塞网页上其他资源的加载。有时候这种阻塞是必要的,因为javascript可能会改变页面结构,进而对后续的资源(css,js)的作用产生影响。但是,当我们能够识别那些对页面结构不产生影响的javascript并且不希望阻塞其他资源时,我们就需要认真的研究一下,javascript异步加载的方式了。
1. 并行的下载脚本
(1) XHR eval
通过XHR技术我们也可以异步地获取js脚本,并通过eval()执行。
var xhrobj = getXHROject();getXHROject.onreadystatechange = function(){ if(xhrObj.readyState ==4 && 200 == xhrObj.status){ eval(xhrObj.responseText); }};xhrObj.open(‘GET’,’A.js’,true)xhrOjb.send(‘’);
由于XHR请求不能跨域,所以脚本必须和主页部署在相同的域中,脚本可并行下载,而且不阻塞其他资源,但是无法保证多个脚本的执行顺序。
(2) script dom element
我们也可以直接在浏览器中插入script dom节点。
var scriptElem = document.createElement(‘script’);document.getElementsByTagName(‘head’)[0].appendChild(scriptElem);
这种方式允许跨域加载js脚本,不阻塞其他资源下载。只有 Firefox 和 Opera 保证脚本按文档中出现的顺序执行,其他浏览器需要工程师自己在代码层面实现执行顺序的控制。Requirjs就是这样实现的。
(3) document write script tag
document.write("");
注意script Tag和script dom的区别,scritp Tag可以保证多个脚本并行加载,但是会阻塞其他资源并行下载。这种方式可以保证脚本按文档中出现的顺序执行
(4) defer和async属性
目前大多数浏览器已经defer和async属性。
1. 如果 async="async":脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)2. 如果不使用 async 且 defer="defer":脚本将在页面完成解析时执行3. 如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本
async="async"不会阻塞其他资源,但是无法保证脚本的执行顺序。defer="defer"阻塞其他资源的加载,并且可以保证脚本的执行顺序,但是要到页面解析完成后才开始执行脚本。
这么多种异步加载javascript脚本的方式,各有利弊,接下来研究一下如何控制脚本之间执行的顺序。
2.脚本的执行顺序
(1)保证行内脚本和外部脚本的执行顺序
当外部脚本按常规方式加载时,它会阻塞行内脚本的执行,可以保证顺序。但是脚本通过上述的几种方式异步加载时,就无法保证行内脚本和异步脚本之间的顺序。下面就讲解一下保证行内脚本和外部脚本保证执行顺序的技术。
[1].硬编码回调
如果web开发者能够控制外部脚本,可以在外部脚本回调行内脚本。
[2]onlode事件
添加script dom节点时,监听加载事件,当脚本成功加载时调用callback(外部脚本)函数。
//行内函数function callback(){ Console.log(‘calllback’);}//异步加载函数function loadScript(url, callback){ var script = document.createElement ("script") script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; document.getElementsByTagName("head")[0].appendChild(script);}//控制行内脚本和外部脚本的执行顺序loadScript('a.js',callback);
[3]定时器
通过定时检查外部脚本的相应变量是否定义,可以判断外部脚本是否加载并执行成功。
这三种方法都可以保证行内脚本和外部脚本之间的执行顺序。其实最难的是保证多个外部脚本之间的执行顺序,这也是我们接下来要看的内容。
(2)保证多个外部脚本之间的执行顺序
[1]. 同域中的脚本
对于同域中的多个外部脚本,可以使用XHR的方式加载脚本,并通过一个队列来控制脚本的执行顺序。
[2]对于不同域的脚本
script dom element 可以异步脚本脚本,不阻塞其他资源,并且在firefox和opera可以保证执行顺序;而document write script 可以异步加载脚本,会阻塞其他资源,在所有浏览器都可以保证执行顺序。因此我们可以根据浏览器选择以上两种方案来控制 不同域的脚本的执行顺序。
'); if(onload){ if(elem.addEventListener){//others elem.addEventListener(window,'load',onload); }else if(elem.attachEvent){ //IE elem.addEventListener(window,'onload',onload); } } } //根据浏览器选择浏览器加载js的方式 loadScript: function(url,onload){ if(-1 != navigator.userAgent.idexOf('Firefox') || -1 != navigator.userAgent.indexOf('Opera')){ //当浏览器为firefox和opera时通过Script Dom Element 保证脚本执行顺序 DomTag.script.loadScriptDomElement(url,onload); }else{ //当为其他浏览器时,通过document write Script保证脚本执行顺序。此时脚本的加载会阻塞其他资源,这是一种折衷 DomTag.script.loadScriptDomWrite(url,onload); } } }ScriptLoader.script.loadScript('A.js',initA);ScriptLoader.script.loadScript('B.js',initB);
转载地址 https://foio.github.io/javascript-async/