JS异步加载的三种方案

  • js加载的缺点:加载工具方法不必阻塞文档,个别js加载会影响页面效率,一旦网速很差,那么整个网站将等待js加载而不进行后续渲染等工做。
  • 有些工具方法须要按需加载,用到再加载,不用不加载。

 1、defer与async

 1.defer是IE独有的一种js异步加载模式,经过src加载的JS会等到文档解析完才会执行(document.readyState="interactive")。在script标签内部也能够写入内部JS代码,可是直接写入的内部JS代码会同步执行。javascript

<script src="https://cdn.staticfile.org//vue/2.2.2//vue.min.js" type="text/javascript" charset="utf-8" defer=""defer">
    //写这里的代码是同步执行
</script>

2.asyn是W3C的标准异步加载模式,经过src加载的JS要等全部资源加载完之后执行(例如:img)(docuement.readyState="complete"),经过asyn异步加载的外部JS才会执行,可是asyn模式的script标签内不能写JS代码。css

<script src="https://cdn.staticfile.org//vue/2.2.2//vue.min.js" type="text/javascript" charset="utf-8" async="async">
    //这里不能写代码
</script>

由于asyn是W3C标准,IE9以前都不兼容,因此仍是很鸡肋。vue

 2、经过插入script标签的方式实现兼容模式的JS异步加载

 经过插入script标签的方式实现JS异步加载的原理其实很简单,直接来代码:java

<script>
    function loadScript(url){
        var script = document.createElement("script");//新建一个script标签
        script.type="text/javascript";//添加type属性
        //绑定src,实现异步加载
        script.src = url;
        //document.head.appendChild(script);切勿这么作,IE7和IE5上document没有head的DOM对象属性。
        var head = document.getElementsByTagName("head")[0];
        //插入script标签
        head.appendChild(script);//把script添加到domTree节点中,而且会触发执行
    }
    loadScript("xxx.js");
</script>

异步加载的JS会何时执行呢?执行会不会阻塞HTML解析呢?基于异步加载方法来一波测试:chrome

<head>
....
//这里是异步加载的代码
<link rel="stylesheet" type="text/css" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script>
    console.log("告诉我何时执行的");
</script>
</head>
-----------------------------------------------------------------------------
//这里是异步加载的外部JS文档内容
var n =Number(new Date());
var n2 = Number(new Date());
while((n2 - n) < (10*1000)){
        n2 = Number(new Date());
        console.log(1);
 }

测试执行的结果:bootstrap

---- 打印39733次1网络

---- 告诉我何时执行的app

上面的测试结果告诉了咱们,经过插入script异步加载的JS,加载完之后就会当即执行。而且执行的时候会阻塞HTML解析。dom

先来讲说我为何要在中间加入一行外部CSS代码,咱们知道CSS是异步加载,可是CSS的加载会阻塞内部JS的执行,因此足够我测试的外部JS加载了。也就是说在内部JS执行前,外部JS确定加载完成了,若是外部JS不会阻塞HTML解析也就不会在内部JS以前执行,可是测试的结果是经过添加script标签异步加载的外部JS在内部JS以前执行了,因此就说明了这种异步加载JS的方式加载完之后就会当即执行,而且若是HTML没有解析完就会阻塞HTML解析。异步

那为何还须要这样的外部加载方式呢?

咱们知道同步加载JS会阻塞HTML解析,而网络因素又是很是不可控的因素,若是某个同步加载的外部JS出现网络不顺畅通,就会一直阻塞页面。若是是一些工具脚本,不须要修改DOM的话,或者是一些不是必定须要的脚本(好比某个特定事件的JS脚本就能够用这种方式来加载),甚至能够在页面渲染完之后经过某个事件触发来加载它。

既然是异步加载的JS脚本,咱们就有必要知道它何时加载完了,好方便咱们使用,因此下面再来丰富如下这个异步加载JS的方法:

function loadScript(url,callback){
    var script = document.createElement("script");//新建一个script标签
    script.type="text/javascript";//添加type属性
    if(script.readyState){
        script.onreadystatechange = function(){
            //script.readyState发生改变时触发script.onreadystatechange
            if(script.readyState == "complete" || script.readyState == "loaded"){
                //script.readyState -->状态码初始值是“loading”,根据文件加载进度,值发生改变
                //IE经过script.readyState的状态码监听异步文件加载进度
                tools[callback]();//采用对象属性调用须要执行的方法 -- 这里按须要使用
          //看到有前辈说IE7和IE5有时候可能会即有"complete",又有"loaded"状态,为了防止万一能够在这里清除监听事件
         script.onreadystatechange = null; } } }else{ script.onload = function(){//script.onload-->表示加载完成之后执行 //除IE之外 Safari chrome firefox opera都兼容 tools[callback](); } } //异步加载文件,必须放在事件添加后面,要否则出现先加载完了,事件函数没有绑定的的状况就不会触发了 script.src = url; //document.head.appendChild(script);切勿这么作,IE7和IE5上document没有head的DOM对象属性。 var head = document.getElementsByTagName("head")[0]; head.appendChild(script);//把script添加到domTree节点中,而且会触发执行 }

W3C标准中有onload事件能够监听外部脚本是否加载完成,因此IE就须要兼容。

IE的script元素的DOM对象上有readyState属性记录外部资源的加载情况,与document对象上的readyState属性同样,有四个值:

uninitialized -- 还未开始载入;

loading - 载入中;

interactive - 已加载,文档与用户能够开始交互;

complete - 载入完成;(有些版本是loanded,据说有些版本这两个状态都会出现本人没遇到过

tools[callback](),是我测试回调外部脚本的方法,这个不重要,主要看需求,反正这里就是说明外部JS已经加载完毕了,能够处理本身要处理的程序了

 凌晨了,晚安。