好,不说废话,要凌晨了我快点写完,关于JS加载阻塞之类的不做科普,本文也不讨论IE9以下的浏览器。
headjs:“异步、并行加载并按你的顺序执行”,这个是最符合广大码农需求的,只是它越来越臃肿,连CSS Respone什么的都加进去了,没必要那么复杂
Loadjs:今天刚刚出现在OSC首页本人疯狂测试后发现,如果需要并行加载就是乱序的(哪个JS先下载完就先执行谁),如果要顺序下载就不能并发,经测试是下载一个执行一个,每个JS文件之间有10ms的空隙(JS下载和加载时所有渲染操作都要暂停,连下载都不行)
还有一个不说了,那是给No-Backend的网站专用的,很庞大一库,功能抄复杂
综上所述,如果你在乎性能和代码质量,一个都不要用
然而这些库都解决了一个致命问题——内联JS的异步加载,这就很纠结了对吧
然而,在现代浏览器环境下,很多原来性能障碍都解决了,新的障碍又特么出来了
1)所有现代浏览器(IE9+),都有“JS预加载器”其实本质是“预下载”,在渲染DOM的同时把所有网页资源都下载好(JS、css、img等等)
2)关于并行下载呢,经测试,所有浏览器都可以同时下载10-20个资源文件,比原来2-6个多的多了,作为现代的JS加载器没必要考虑并行下载的问题
3)现在大部分码农都只是不用或者只用JQuery里面的一部分了吧,追求性能是必须的,当然随着项目越来越庞大,引入Angular,Ember之类的巨型框架也是没办法,下载慢到不担心,关键执行慢卡死在那里结果用户一直白屏
4)defer="defer"已被所有浏览器支持,只是只适用于外部JS,内联JS只在IE有效,这正是本文要解决的问题
5)因为JS MVC框架的模板问题,很多JS代码不得不写在HTML里以获得服务器模板引擎的渲染(我还没看见过谁渲染一遍JS的,当然node除外哈。。)
好,show code
因为Riot.js,Angular.js这类框架对JS文件的加载时间段有很高的要求,所以用他们做demo再合适不过
1)给所有的外部JS加上 defer="defer"
<script src=‘riot.min.js‘ defer=‘defer‘></script> <script src=‘todo.js‘ defer=‘defer‘></script>
2)给内联JS封装一个DOMContentLoaded,现代浏览器都提供了100%这个事件,然而并没有封装。。。。不像window.load那样直接可用,把下面的代码放入一个外部JS,并且不要使用 defer="defer"
//保存domReady的事件队列 eventQueue = []; //判断DOM是否加载完毕 isReady = false; //判断DOMReady是否绑定 isBind = false; /*执行domReady() * *@param {function} *@execute 将事件处理程序压入事件队列,并绑定DOMContentLoaded * 如果DOM加载已经完成,则立即执行 *@caller */ function domReady(fn){ if (isReady) { fn.call(window); } else{ eventQueue.push(fn); }; bindReady(); }; /*domReady事件绑定 * *@param null *@execute 浏览器通过addEvListener绑定DOMContentLoaded,包括ie9+ *@caller domReady() */ function bindReady(){ if (isReady) return; if (isBind) return; isBind = true; document.addEventListener(‘DOMContentLoaded‘,execFn,false); }; /*执行事件队列 * *@param null *@execute 循环执行队列中的事件处理程序 *@caller bindReady() */ function execFn(){ if (!isReady) { isReady = true; for (var i = 0; i < eventQueue.length; i++) { eventQueue[i].call(window); }; eventQueue = []; }; };
如果非要支持IE6-8,那这里有个完整的demo http://dengo.org/archives/1037
亲测表示这个比Jquery的document.ready快!
其实这个时候JQuery的document.ready是用不了的,因为JQ还没被加载呢,除非你不对JQ异步加载,这样异步加载的意义就不大了
3)对于内联JS代码,以后统一用 domReady(function(){ // });包裹
//内联JS 1 domReady(function(){ riot.mount(‘todo‘, { title: ‘I want to behave!‘, items: [ { title: ‘Avoid excessive coffeine‘, done: true }, { title: ‘Hidden item‘, hidden: true }, { title: ‘Be less provocative‘ }, { title: ‘Be nice to people‘ } ] }) });
这个domReady()可以调用多次,直接用就行
好吧我科普一下,这个DOMContentLoaded就是在DOM结构加载完就触发(就是chrome developer tools -> TimeLine 里的那根蓝线),而window.load是在整个页面全部加载完时触发(就是chrome developer tools -> TimeLine 里的那根红线,在用户看来就是小菊花不转的时候)
好这样就解决了所有问题,结论就是,如果你需要引入很多庞大的库,又不想过于影响页面的首次加载速度(白屏时间),这个方案适合所有网站,嗯,用吧。
本文按cc-署名-以相同方式共享传播