大家都知道浏览器加载外部文件(css和js),都是呈阻碍式加载,试想如果外部引入文件过多,外加程序js执行时间和http请求/相应的延迟,会使页面的空白时间加长,影响用户体验。有的人可以说,可以把页面所需的css文件放入<head/>标签中,js文件放入<body/>内尾部,这样可以在页面部分显示后再加载和执行js文件,提高用户体验,确实这是一种有效的解决方案。
随之而来,将js文件放入尾部,如果文件数量小,但如果引入的文件数量挺大,就会造成页面的交互失灵,因为js文件在用户点击页面是,页面引入的js文件还没有完全加载并执行。
解决方法大概有两种方式:
(1)如果可以尽量将需引入的js文件合并为一个文件,减少浏览器对服务器的请求次数,减少延迟时间,提高js的加载和执行速度。
(2)另一个解决方案就是解决js文件加载和执行的阻塞特性(重点终于出来了),希望js文件的加载可以解决浏览器的阻塞特性:
1.<script/>标签的defer属性
在<script/>标签中添加defer属性可以使在<head/>中引入的js文件,在页面加载的时候不加载/执行,而是等页面加载完成后才加载执行(也就是window.onload事件促发时)。其实defer的功效和把js文件放入<body/>后的效果是一样的,但它确实现了打破浏览器的引入阻碍特性。但并不是所有浏览器都支持defer素性,除IE和FF3.1支持外,其他浏览器都将无视defer属性。
2.实时创建<script/>标签
实现的方式,并不是一开始把大量的js文件引入文件中,而是在需要时再引入所需的js文件。引入如下函数即可实现:
function deferLoad(url,fn){
var script = this.element("script");
script.type = "text/javascript";
script.src= url;
args = Array.prototype.slice.call(arguments,2);
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
fu.apply(this,args);
}
};
}else{
script.onload = function(){
fu.apply(this,args);
};
}
document.getElementsByTagName("head")[0].appendChild(script);
};
上面封装的deferLoad函数包含了大量的信息:
args = Array.prototype.slice.call(arguments,2);
将函数中的参数转换成一个真正的数组,auguments虽然有length属性,但它并不是一个纯正的Array对象,同样不具有数组又有的方法。Array.prototype.slice.call可以将参数转化为一个数组,第二个参数的设定可以从第几个值开始生成参数数组。
例如
deferLoad(1.js,function(){alert("aa")},"bb","cc","dd");
args数组的值为["bb","cc","dd"
]。
<script/>标签生成并插入到<head/>中,文件引入成功后,开始执行交互,但问题是,怎么才能知道文件加载成功,script标签为我们提供了load事件,所以,我们可以将回调函数,放在script.onload中。
script.onload = function(){
fu.apply(this,args);
};
没有兼容性问题,开玩笑了吧,有脚趾头想一下,IE下肯定有问题,IE 下script事件为onreadystatechange,这下上面函数中的if(script.readyState)便能明白。
通过这种方式引入的js文件如果有依赖,可以通过一下方式引入:
如果2.js对1.js文件有依赖,可以这样引入,
deferLoad("1.js",function(){
deferLoad("2.js"),function(){
alert("wjr");
}
})
3.通过异步的方式引入文件
通过ajax的方式来引入文件,也可以解决加载阻隔的问题,但如果多个文件之间有依赖关系,这种引入方式便会出现问题。
好吧,知道的解决方式就这些了,大家可以根据实际情况,灵活使用。
ok,本文结束!