0%

加载和执行

发布于 / 分类: javascript高性能 / 暂无评论 / 阅读量 245

最后更新于 2019年05月24日

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. 使用无阻塞加载的方式
js深度克隆 数据存取
  • 滑动验证
    »
  • weixiao kaixin tushetou jingkong deyi fanu liezui liuhan daku ganga bishi nanguo lihai qian yiwen numu tu yi haixiu se fadai minyan hehe henkaixin huaji biyiyan kuanghan maimeng shui xiaku penqi zhangzui pen aini ye niu laji ok chigua renshi kongbu shuai xiaoxiese touxiao huaixiao jingnu chihuai kaisang xiaoku koubi zhuangbi lianhong kanbujian shafa zhijing xiangjiao dabian yaowan redjing lazhu rizhi duocang chixigua hejiu xixi xiaopen goukun xiaobuchu shenme wusuowei guancha lajing chouyan xiaochi bie zhadanzui zhadanxiao