靠谱的软件下载站
当前位置:  IEfans/IE专区/IE相关/详解主流浏览器多个外部JS请求和执行机制

详解主流浏览器多个外部JS请求和执行机制

IE相关 互联网 2010-12-14 阅读(4371)
在IE8、Firefox3.6之前页面加载外部的javascript文件(IE6和IE7会连同图片,样式资源和页面渲染一同阻塞)是阻塞式的,而在之后的版本中,浏览器都使用了瀑布式加载,这样页面的打开及渲染速度都会变快,请注意,我提到的瀑布式加载,仅仅指的是加载,而非JS的执行,在主流浏览器中JS的执行总是阻塞的。用简单一点的语言描述,就是同一时间,页面只会加载一个js文件。在第一个js文件加载并执行完之前,第二个要引入的js不会下载和执行。而页面中js的引入顺序以请求的顺序为定。 我们来看一个在IE6浏览器下,页面加载多个js文件的例子: inline script block:为页面内嵌js代码块,而external script则是需要从外部加载进来的js文件。 在IE6中演示加载多个js文件的网页 我们设定inline script执行用时3秒,external script1加载用时2秒,执行用时3秒,external script2加载用时2秒,执行用0秒(实际上会稍大于0,下同),external script3加载用2秒,执行用时0秒。 由上图,可知页面需要15641毫秒才能将js加载并执行完。我们可以得到一个公式: 计算加载时间 而同样的页面,在IE8Firefox3.6下,我们会得到: IE8: (只需要9秒,速度快了近一倍) 在IE8中演示加载多个js文件的网页 Firefox: 在Firefox中演示加载多个js文件的网页 由以上的现象,可以证实上面的观点。 那么是否有一个方案可以让IE6/IE7或是Chrome展示我们的页面更快一点,可以同时加载多个文件,并且不影响页面中DOM元素的渲染?答案是肯定的。 在之前的项目中,我曾经写过类似的代码,来处理请求多个外部javascript文件。
function loadScript(url, fn, doc, charset){ doc = doc || document; var script = doc.createElement('script'); script.language = "javascript"; script.charset = charset?charset:’utf-8’; script.type = 'text/javascript'; script.onload = script.onreadystatechange = function(){ if (!script.readyState || 'loaded' === script.readyState || 'complete' === script.readyState) { fn && fn(); script.onload = script.onreadystatechange = null; script.parentNode.removeChild(script); }; }; script.src = url; $('head')[0].appendChild(script); }
那个当网页中需要动态调用多个js文件时,我们可以这样写:
loadScript('../jquery-1.4.2.js',function(){console.log('jquery-1.4.2.js loaded')}); loadScript('$.wbx.js',function(){console.log('example/$.wbx.js loaded')}); loadScript('gadgets.js',function(){console.log('example/gadgets.js loaded')}); loadScript('jui-all.js',function(){console.log('example/jui-all.js loaded')});
上段代码处理这样的功能:可以动态加载多个js文件,当文件下载完成后可以执行回调函数。当然它的好外不止于此,我们还可以按需获取,减少流量和浏览器内存占用!对于高并发请求的网站,有着天大的好处!我以前在的一家公司做过统计首页减少50k的流量,一年就可以节省十几万的成本呀。。。但上面的代码也不是完美的,在一些应用下会出现新的问题。请看下面的截图 第一次执行的效果 第二次执行的效果 不难发现,加载的顺序变了!这时,如果a.js依赖于b.js,而b.js偏偏又迟于a.js加载完成时,就会出现“变量未定义”的js错误,无法执行下去。 那我们的下一个目标就是如何让这些外部文件有序的加载进网页。这样无论智商高的还是低的,都会想到 队列 ,不错,就是队列。在JS里实现队列并不难办,是的,数组就行,我们可以用数组的shift方法,弹出数组的第一个JS文件(这也是我们最先加入数组的那个JS文件),模拟了队列的实现方法。不过,在这里我们还要多考虑一个问题:必须要上一个js文件加载完成后,下一个JS的请求才能开始。我们怎么判断上一个JS文件已经加载完成了呢?上代码:
var testNode = doc.createElement('script'), fn, node; fn = testNode.readyState ? function(node, callback){ node.onreadystatechange = function(){ var rs = node.readyState; if (rs === 'loaded' || rs === 'complete') { // handle memory leak in IE node.onreadystatechange = null; callback.call(this); } }; }: function(node, callback){ node.onload = callback; };
在非IE浏览器的情况下我们可以很方便的用dom元素的onload和onerror来判断元素是否已经加载完成,而IE不吃这套,它并不支持script节点的onload判断,所以我们只好寻求其它的解决方案。还好,IE对onreadystatechange和readyState的支持足以让我们完成这个任务。 readyState 的值  可能为 以下几个 : “uninitialized” – 原始状态 “loading” – 下载数据中.. “loaded” – 下载完成 “interactive” – 还未执行完毕. “complete” – 脚本执行完毕. 在IE6/IE7/IE8下,虽然script节点加载完成,但结果并不总是loaded或是complete,并且先设置src再append到节点树和先append到节点树再设定src,IE7/IE8处理加载src文件的时间是不同的。为了减少不必要的麻烦,我们这里就两个状态都判断了。 接下来还有一个问题,如果其中某个js文件,需要去处理dom节点,而我们在加载js时并不能确定这个节点是否已经渲染完成,既我们的Js加载需要在所有的dom节点渲染完成后才开始加载执行,这时我们就要用到经典的domReady判断。
function domReady(){ if (readyBound) { return; } readyBound = true; if (document.readyState === "complete") { return dequeue(); } if (document.addEventListener) { document.addEventListener("DOMContentLoaded", dequeue, false); window.addEventListener("load", dequeue, false); } else if (document.attachEvent) { document.attachEvent("onreadystatechange", dequeue); window.attachEvent("onload", dequeue); var toplevel = false; try { toplevel = window.frameElement == null; } catch (e) { } if (document.documentElement.doScroll && toplevel) { try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ document.documentElement.doScroll("left"); } catch (error) { setTimeout(arguments.callee, 10); return; } dequeue(); } } }
早期的做法是用window.onload或是defer,defer并不是所有的浏览器都支持,可以排除。window.onload事件是待到页面上的所有资源被加载才激活,如果页面上有许多图片或音乐,而我们要操作的元素在的它们的下方呢?因此,W3C搞了DOMContentLoaded与addEventListener,只是可惜IE又不支持。还好,我们又找到readystatechange。知道页面的内容加载完成,我们再用到Diego Perini提供的doScroll来判断document节点是否渲染到页面。这样我们就能够让js在页面渲染完成后再执行了。至些我们就实现了以下的功能:
  1. 异步加载,加快了页面的加载时间,同时能够按需加载,节省流量
  2. 有序加载,解决js依赖问题
  3. 延时执行,在页面渲染完成后再执行js,防止undefined情况
附上最后实现的代码:nc.loader.rar via:福克

标签:JS主流浏览器IE Web

Copyright © 1998-2017 www.iefans.net All Rights Reserved 湘ICP备13012168号-17