目前业界商用的JS文件加载器有LABjs,Require.js,Sea.js。后两者同时又是模块加载器,很多网站/软件并不是按照AMD/CMD规范来开发的,只有LAB.js在大部分网站/软件上可以即插即用,下面分析一下LAB.js。
LAB.js即loading and blocking,并行的加载脚本文件,同时同步的等待执行。
实例:
$LAB.setGlobalDefaults({Debug:true}) //打开调试 $LAB //第一个执行链 .script(‘http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js‘) .script(‘http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js‘) //第二个执行链 .wait(function(){ // console.log(window.$) // console.log(window._) }) //第三个执行链 .script(‘http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.1/js/bootstrap.min.js‘) .script(‘http://cdnjs.cloudflare.com/ajax/libs/fancybox/2.1.5/jquery.fancybox.pack.js‘) //第四个执行链 .wait(function(){ // console.log(plugin1Function) // console.log(plugin2Function) }) //第五个执行链 .script(‘./module1.js‘) .script(‘./module2.js‘) //第六个执行链 .wait(function(){ // console.log(module1Function) // console.log(module2Function) })
调试信息跟踪分析:
//这三个执行是并行加载完以后,执行的3个wait操作 驱动执行的函数advance_exec_cursor, chain length:2 exec_cursor:0 LAB.js:46 驱动执行的函数advance_exec_cursor, chain length:4 exec_cursor:0 LAB.js:46 驱动执行的函数advance_exec_cursor, chain length:6 exec_cursor:0 LAB.js:46 //因为request_script函数中setTimeout(f,0),所以加载会滞后于wait操作 start script load (ordered async): http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js LAB.js:46 start script load (ordered async): http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js LAB.js:46 start script load (ordered async): http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.1/js/bootstrap.min.js LAB.js:46 start script load (ordered async): http://cdnjs.cloudflare.com/ajax/libs/fancybox/2.1.5/jquery.fancybox.pack.js LAB.js:46 start script load (ordered async): file:///D:/Users/sj_yu/Desktop/work/20140408/LABjs/./module1.js LAB.js:46 start script load (ordered async): file:///D:/Users/sj_yu/Desktop/work/20140408/LABjs/./module2.js LAB.js:46 script execution finished: http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js LAB.js:46 script execution finished: http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js LAB.js:46 //按顺序执行完第一组执行链,也就是jquery和lodash,执行完JS后,调用advance_exec_cursor来探测执行游标是否需要移动 驱动执行的函数advance_exec_cursor, chain length:6 exec_cursor:0 LAB.js:46 //执行第二组执行链 $LAB.wait() executing: function (){ // console.log(window.$) // console.log(window._) } LAB.js:46 //执行第三组执行链 script execution finished: http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.1/js/bootstrap.min.js LAB.js:46 script execution finished: http://cdnjs.cloudflare.com/ajax/libs/fancybox/2.1.5/jquery.fancybox.pack.js LAB.js:46 //执行完第三组后,触发advance_exec_cursor 驱动执行的函数advance_exec_cursor, chain length:6 exec_cursor:2 LAB.js:46 //第四组执行链 $LAB.wait() executing: function (){ // console.log(plugin1Function) // console.log(plugin2Function) } LAB.js:46 //第五组执行链 script execution finished: file:///D:/Users/sj_yu/Desktop/work/20140408/LABjs/./module1.js LAB.js:46 script execution finished: file:///D:/Users/sj_yu/Desktop/work/20140408/LABjs/./module2.js LAB.js:46 驱动执行的函数advance_exec_cursor, chain length:6 exec_cursor:4 LAB.js:46 //执行第六组执行链 $LAB.wait() executing: function (){ // console.log(module1Function) // console.log(module2Function) }
LAB.js关键代码分析:
//核心代码 (function(global){ function create_sandbox() { //加载脚本,用到的方式有document.createElement(‘script‘),xhr function do_script() { //脚本加载 if(需要预加载) { if(是否默认支持预加载) { if(是否支持preload属性) { script.preload = true; } else { script.onreadystatechage = function() { if (script.readyState == "loaded") onload(); } } script.src = src; } else if(是否用XHR方式) { new XMLHttpRequest(); } else { script.type = "text/cache-script"; create_script_load_listener(); script.src = src; } } else if(可以异步加载) { script.async = false; //强制同步执行 create_script_load_listener(); script.src = src; } else { create_script_load_listener(); script.src = src; } } //创建执行链 function create_chain() { var chain = [];//执行链 //执行完JS调用该函数 function chain_script_executed() { advance_exec_cursor(); } //主动执行每个执行链 function advance_exec_cursor() { while(执行游标 < chain.length) { if(执行链是wait中的函数) { 执行wait函数 } else if(该执行链还没执行完) { } 游标++; } if (游标 == chain.length) { 执行停止 } } //执行链api var chainedAPI = { script: function() { do_script(); return chainedAPI;//支持链式调用 }, wait: function() { if(arguments.length > 0) { chain.push(arguments) } advance_exec_cursor(); return chainedAPI;//支持链式调用 } } return { script:chainedAPI.script, wait:chainedAPI.wait }; } //end of create_chain //整个$LAB的接口 var instanceAPI = { script:function(){ return create_chain().script.apply(null,arguments); }, wait:function(){ return create_chain().wait.apply(null,arguments); } }; return instanceAPI; } // end of create_sandbox //create_sandbox()返回 instanceAPI,包含script,wait等接口 global.$LAB = create_sandbox(); })(this);
总结:
优点:LABjs的接口设计非常轻,api很清晰,现有项目可以快速上手,大幅改进页面性能。相对于多个JS文件压缩到一个JS文件,又有不阻塞图片,css加载和模块化的优势。
缺点:目前没有靠谱的grunt插件,打包困难。
与Require.js和Sea.js的比较请参考:
http://www.zhihu.com/question/20342350
DailyJS对LABjs的解析:
http://dailyjs.com/2011/08/15/code-review/