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已經加載完畢了,能夠處理本身要處理的程序了

 凌晨了,晚安。

相關文章
相關標籤/搜索