(图片来源:http://www.cnblogs.com/sharpxiajun/p/4072396.html)
这是清除浏览器缓存后,第一次访问百度首页的场景,在Firefox的瀑布图中可以看到:javascript文件下载完毕后,有一段时间没有请求处理。这段时间过后,请求才会接着执行,这段时间就是HTTP请求阻塞。
当浏览器遇到<script>标签时,浏览器会停止处理页面,先执行Javascript代码,然后再继续解析和渲染页面。在这个过程中,页面和用户的交互完全被阻塞了。通常表现为显示空白页面,用户无法浏览内容。
现在的浏览器都支持并行下载文件,比如上图同时下载多个图片。而且,IE8 、FireFox3.5、Safari4和Chrome2都支持并行下载javascript文件,但是javascript文件的下载会阻塞其他资源的下载。尽管脚本的下载过程不会相互影响,但是页面仍然需要等待javascript的执行完毕才能继续。所以,新浏览器通过并行下载提高了性能,但是脚本阻塞问题仍然没有解决。
解决方法有:
(1)把<script>标签放到</body>闭合之前
由于脚本会阻塞其他资源的下载,因此推荐把所有的<script>标签尽可能的放到<body>标签的底部。
(2)脚本合并,减少<script>标签的数量
每次遇到<script>标签,浏览器都要发一次HTTP请求。而HTTP请求耗时是web性能的最大的影响之一。
(3)动态脚本加载
忙指示(忙指示现象有:浏览器选项卡的旋转圆圈,鼠标变成漏斗状,浏览器左下状态栏显示正在加载某某url)标记结束了,就表明页面的加载操作结束了,为了防止js脚本阻塞页面的加载。我们就要让那些不用于页面初始化展示的js代码的加载和执行在浏览器忙指示结束后触发。结束的标志就是onload事件触发,因此我们可以把那些有页面加载无关的js脚本放到onload事件里。
<span style="font-size:14px;"> var script=document.createElemetn("script"); script.type="text/javascript"; script.src="file.js";; document.getElementsByTagName("head").appendChild(script);</span>
使用动态脚本这种技术重点在于:文件的下载和执行过程不会阻塞页面其他进程。通常要把新创建的<script>标签添加到<head>标签里比添加到<body>标签里更保险。但是这种情况下,必须跟踪并确保脚本下载完成并准备就绪。
标准浏览器:
符合w3c标准的浏览器,script元素接收完成式,触发一个load事件。
var script =document.createElement("div"); script.type="text/javascript"; script.onload=function(){ alert("ok"); }; script.src="file.js"; document.getElementsByTagName("head")[0].appendChild(script);
IE支持另外一种实现方式,触发readystatechange事件。<script>元素提供一个readyState属性,它的值在外链下载过程中不同阶段发生变化,属性有五个值:
“uninitializd” 初始状态
“loading” 开始下载
“loaded” 下载完成
“interactive” 数据下载完成但尚不可用
“complete” 所有数据已准备就绪
最有用的的两个状态是:"loaded"和"complete"。
var script=document.createElement("script"); script.type="text/javascript"; //IE script.onreadystatechange=function(){ if(script.readyState=="loaded"||script.readyState="complete"){ script.onreadystatechange=null; alert("ok"); } }
兼容标准及IE浏览器:
function loadScript(url ,callback){ var script=document.createElement("script"); script.src="text/javascript"; if(script.readyState){//IE script.onreadyStateChange=function(){ if(script.readyState=="loaded"||script.readyState=="complete"){ script.onreadystatechange=null; callback(); } }; }else{//其他浏览器 script.onload=function(){ callback(); }; } script.src=url; document.getElementsByTagName("head")[0].appendChild(script); }
单个文件:
loadScript("file1.js",function(){ alert("ok") });
添加多个文件:
loadScript("file1.js",function(){ loadScript("file2.js",function(){ loadScript("file3.js",function(){ alert("ok"); }); }); });
添加多个文件会对管理带来困难,可以借助jquery的$.Deferred()和$.when()避免回调地狱。(http://blog.csdn.net/vuturn/article/details/45024767)
推荐的无阻塞模式
<script type="text/javascript" src="test.js"></script> <script type="text/javascript"> loadScript("file.js",function(){ alert("ok"); }); </script>
参考:(http://www.cnblogs.com/sharpxiajun/p/4072396.html)
《高性能JavaScript 》