异步加载JS

 平时最常使用的就是这种同步加载形式:
<script src=”http://yourdomain.com/script.js”></script>
同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行。
js 之所以要同步执行,是因为 js 中可能有输出 document 内容、修改dom、重定向等行为,所以默认同步执行才是安全的。
以前的一般建议是把<script>放在页面末尾</body>之前,这样尽可能减少这种阻塞行为,而先让页面展示出来。
简单说:加载的网络 timeline 是瀑布模型,而异步加载的 timeline 是并发模型。

常见异步加载(Script DOM Element)

[javascript]

  1. (function() {   
  2. var s = document.createElement(‘script’);   
  3. s.type = ‘text/javascript’;   
  4. s.async = true;   
  5. s.src = ‘http://yourdomain.com/script.js’;   
  6. var x = document.getElementsByTagName(‘script’)[0];   
  7. x.parentNode.insertBefore(s, x);   
  8. })();   

例如 Google Analytics 和 Google+ Badge 都使用了这种异步加载代码:

[javascript]

  1. (function() {   
  2. var ga = document.createElement(‘script’);   
  3. ga.type = ‘text/javascript’; ga.async = true;   
  4. ga.src = (‘https:’ == document.location.protocol ? ‘https://ssl’ : ‘http://www’) + ‘.google-analytics.com/ga.js’;   
  5. var s = document.getElementsByTagName(‘script’)[0]; s.parentNode.insertBefore(ga, s);   
  6. })();   
  7.   
  8. (function()   
  9. {var po = document.createElement(“script”);   
  10. po.type = “text/javascript”; po.async = true;po.src = “https://apis.google.com/js/plusone.js”;   
  11. var s = document.getElementsByTagName(“script”)[0];   
  12. s.parentNode.insertBefore(po, s);   
  13. })();   

百度地图的异步加载:

[javascript]

  1. var map;  //全局地图  
  2. function initialize() {  
  3.     map = new BMap.Map(‘map’);  
  4.     var point = new BMap.Point(121.491, 31.233);  
  5.     map.centerAndZoom(point, 11);  
  6.     //使用鼠标滚轮控制缩放  
  7.     map.enableScrollWheelZoom();  
  8.     //添加地图控件  
  9.     map.addControl(new BMap.NavigationControl());  
  10.     map.addControl(new BMap.ScaleControl());  
  11.     map.addControl(new BMap.OverviewMapControl());  
  12.     map.addControl(new BMap.MapTypeControl());  
  13.     //点击地图就添加标注点  
  14.     map.addEventListener(“click”, addMarkerService);  
  15.     PanOptions.noAnimation=false;  
  16. }  
  17. function loadScript() {  
  18.     var script = document.createElement(“script”);  
  19.     script.src = “http://api.map.baidu.com/api?v=1.5&ak=imORpY5Kv1BDZpM4ab4QPQM5&callback=initialize”;  
  20.     //此为v1.5版本的引用方式    
  21.     // http://api.map.baidu.com/api?v=1.5&ak=您的密钥&callback=initialize”; //此为v1.4版本及以前版本的引用方式    
  22.     document.body.appendChild(script);  
  23. }  
  24. window.onload = loadScript;  

但是,这种加载方式在加载执行完之前会阻止 onload 事件的触发,而现在很多页面的代码都在 onload 时还要执行额外的渲染工作等,所以还是会阻塞部分页面的初始化处理。

[javascript]

  1. (function() {   
  2. function async_load(){   
  3. var s = document.createElement(‘script’);   
  4. s.type = ‘text/javascript’;   
  5. s.async = true;   
  6. s.src = ‘http://yourdomain.com/script.js’;   
  7. var x = document.getElementsByTagName(‘script’)[0];   
  8. x.parentNode.insertBefore(s, x);   
  9. }   
  10. if (window.attachEvent)   
  11. window.attachEvent(‘onload’, async_load);   
  12. else   
  13. window.addEventListener(‘load’, async_load, false);   
  14. })();   

这和前面的方式差不多,但关键是它不是立即开始异步加载 js ,而是在 onload 时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。
补充:DOMContentLoaded 与 OnLoad 事件
DOMContentLoaded : 页面(document)已经解析完成,页面中的dom元素已经可用。但是页面中引用的图片、subframe可能还没有加载完。
OnLoad:页面的所有资源都加载完毕(包括图片)。浏览器的载入进度在这时才停止。
这两个时间点将页面加载的timeline分成了三个阶段。

异步加载的其它方法
由于Javascript的动态特性,还有很多异步加载方法:
XHR Eval
XHR Injection
Script in Iframe
Script Defer
document.write Script Tag
还有一种方法是用 setTimeout 延迟0秒 与 其它方法组合。
XHR Eval :通过 ajax 获取js的内容,然后 eval 执行。
var xhrObj = getXHRObject();

[javascript]
  1. xhrObj.onreadystatechange =   
  2. function() {   
  3. if ( xhrObj.readyState != 4 ) return;   
  4. eval(xhrObj.responseText);   
  5. };   
  6. xhrObj.open(‘GET’‘A.js’true);   
  7. xhrObj.send();   

标签