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....

总结

  1. 将script元素放入body的底部,这能保证脚本在执行前页面已经完成渲染(甚至不会阻塞文件的下载)。
  2. 可以使用代码压缩和合并节约资源
  3. 使用无阻塞加载的方式
分类: javascript高性能 标签: js无阻塞加载

评论

暂无评论数据

暂无评论数据

目录