加载和执行
js的加载会阻塞页面的加载和渲染,那么常用的方式就是将script元素放置在body元素里面最底下的位置,但是这样还是会有阻塞,但是不会阻塞下载,阻塞的是渲染,也就是说,js运行长时间的话,页面也会长时间显示空白,哪怕所有的资源都已经下载完毕了。
延迟脚本
为此便有了无阻塞的模式,那么最简单的就是给script添加defer和async属性了,但是这两个属性当初也只有ie支持,其他的浏览器并不支持,但是目前为止,如果都是新版本的浏览器,这两个属性已经全面支持了。
defer表示当前的js文件不需要修改dom,所以他会在dom加载完运行,async则是异步加载,谁先加载谁先运行,但是如果js文件有前后顺序的话,就不怎么实用了。
动态脚本
既然defer和async不能满足我们的需求,那么只能另寻它法,于是就有了动态脚本!
动态脚本原理:
在浏览器加载html的时候只有一个script元素,这个元素里的js代码非常少,代码则创建script元素并插入head中,通过代码插入的js文件并不会影响页面的加载和解析。
<script type="text/javascript" src="js/js.js"></script>
//js文件中的代码
function loadScript(url) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
loadScript('js/load.js');
这样通过动态加载的js可以避开阻塞带来的问题,但是也会产生一个新的问题,如果动态加载的js文件包含其他js文件调用的接口,那么就需要判断,当这个js文件加载完毕后再运行这个接口的代码,不然浏览器就会报错。
新版的浏览器支持load事件判断script元素是否加载完成,旧ie不支持,但是支持readystatechange事件,通过readyState属性返回的值判断是否加载完成。
readyState的值有5个,分别为:‘uninitialized’(初始状态)、‘loading’(开始下载)、‘loaded’(下载完成)、‘interactive’(数据完成下载但还不可用)、‘complete’(所有数据准备就绪);
一般来说我们只需要判断是complete即可,但是ie有时候会返回complete,有时候会返回loaded,这两个状态返回后,代码就已经可以使用了,所以我们还要检查这两种状态,只要其中任意一个出现了,就可以触发了。
function loadScript(url,callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
if(script.readyState) {
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('js/load.js');
如果我们要求js1加载完再加载js2,这样顺序加载的话,可以使用callback函数,写法如下:
loadScript('js/load1.js',function(){
loadScript('js/load2.js',function(){
loadScript('js/load3.js',function(){
//无限嵌套
});
});
});
ajax获取
通过ajax获取,可以控制代码什么时候运行,因为通过ajax获取的是js代码本身,但它并不会自动执行,只有当我们插入的时候才会运行,但是也有一个坏处,因为同源策略的限制,你无法通过js去获取cdn等资源上的js文件,当然你也可以通过跨域的方式去解决。
var xhr = new XHRHttpRequest();
xhr.open('get','js/load.js',true);
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
var script = document.createElment('script');
script.type = 'text/javascript';
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
};
xhr.send(null);
ajax还要考虑ie的兼容性,所以他的代码量还要大很多,加上同源策略,在大型的web项目上,并不会考虑ajax注入js。
Lazyload 类库
Lazyload类库也是通过引入外部js文件,这个js文件是经过压缩后的产物,其大小只有1.5kb,所以并不会阻塞页面,然后他也会动态插入script元素,通过通用的一个方法。
<script type="text/javascript" src="lazyload-min.js"></script>
//调用代码
LazyLoad.js('js/load.js',funciton(){
Application.init();
})
Lazyload 还支持多个文件,只需要传入数组即可
//调用代码
LazyLoad.js(['load1.js','load2.js','load3.js'],funciton(){
Application.init();
})
当然还有其他的开源库,比如LABjs....
总结
- 将script元素放入body的底部,这能保证脚本在执行前页面已经完成渲染(甚至不会阻塞文件的下载)。
- 可以使用代码压缩和合并节约资源
- 使用无阻塞加载的方式
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据