由于大多数浏览器使用单一进程处理用户界面刷新和javascript脚本执行,所以浏览器同一时刻只能做一件事情,js执行过程越久,浏览器等待响应的时间就越长。无论是内嵌的js还是外链的js,页面的下载和渲染都会停下来,等待脚本执行完成。
这意味着,如果在head标签中,放入大量的script标签,那么在这些js没有被下载下来并执行完之前,页面渲染和用户交互是不会发生的,它们被完全阻塞掉了。虽然IE8,Firefox3.5,Safari4,Chrome2允许并行下载js文件,但这只节省了js下载的时间,这些脚本下载下来后还有执行的时间。更遗憾的是,只有js脚本下载时互不影响,页面所需的其他资源,比如css,图片依旧要乖乖等待js。因此将脚本放在页面顶部会导致明显的延迟,通常表现为空白页面。
所以我们要把所有的script标签放到body标签的后面,这样能够先把页面大部分内容呈现给用户,增加用户使用体验。
当然还有一些其他的方式来实现无阻塞脚本。
1.HTML4为script标签定义了一个扩展属性:defer,作用是浏览器解析到这些js时只会下载它们,而不会执行,只有到DOM加载完成后,才会回过头来执行这些js。所以带defer属性的script标签可以随意放置在文档任何位置。只可惜,这个属性只有IE4+和Firefox3.5+浏览器支持。
2.动态创建js脚本也是个方法。代码如下:
var script=document.createElement('script');
script.type='text/javascript';
script.src='xx.js';
document.getElementsByTagName('head')[0].appendChild(script);
这时候不管把这个script标签放在哪里,xx.js文件的下载和执行过程都不会阻塞页面进程。
这是因为此时的script标签被当成了一个创建出来的普普通通的DOM元素。
3.XHR脚本注入
代码如下:
var xhr=new XMLHttpRequest();
xhr.open('get','xx.js',true);
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status>=200&&xhr.status<300||xhr.status==304){
var script=document.createElement('script');
script.type='text/javascript';
script.text=xhr.responseText;
document.body.appendChild(script);
}
}
}
xhr.send(null);
解释一下,此方法采用了Ajax技术,整个过程就是创建出xhr对象,用get方法在服务器上请求xx.js文件,一旦请求成功后,xx.js中的内容就会被放在xhr对象的responseText属性中,此时我们像第二种方法一样动态创建出一个script标签,然后把responseText中的内容赋给这个标签即可。相当于创建一个带有内联脚本的script标签。整个请求的过程就是脚本下载的过程,而script标签被添加进页面的位置,决定了脚本执行时候,所以请把这个动态生成的script标签放在body后面。
既然使用了Ajax技术,那么你也猜到了,这种方法存在跨域问题,所以大型WEB应用不会采用XHR脚本注入技术。