天天看點

腳本無阻塞加載政策

腳本無阻塞加載政策

       從上圖可以看出,當浏覽器遇到<script>标簽時,浏覽器會停止處理頁面,先執行Javascript代碼,然後再繼續解析和渲染頁面。在這個過程中,頁面和使用者的互動完全被阻塞了。通常表現為顯示空白頁面,使用者無法浏覽内容。

      現在的浏覽器都支援并行下載下傳檔案,比如上圖同時下載下傳多個圖檔。而且,IE8 、FireFox3.5、Safari4和Chrome2都支援并行下載下傳javascript檔案,但是javascript檔案的下載下傳會阻塞其他資源的下載下傳。盡管腳本的下載下傳過程不會互相影響,但是頁面仍然需要等待javascript的執行完畢才能繼續。是以,盡管最新的浏覽器通過并行下載下傳提高了性能,但是腳本阻塞問題仍然沒有解決。

     解決辦法有:

    1. 把<script>标簽放到</body>閉合之前   

    由于腳本會阻塞其他資源的下載下傳,是以推薦把所有的<script>标簽盡可能的放到<body>标簽的底部,以盡量減少對整個頁面下載下傳的影響。這是雅虎性能小組提出的優化JavaScript的首要規則:把腳本放在底部。

    2. 腳本合并,減少<script>标簽的數量  

    每次遇到<script>标簽,浏覽器都要發一次HTTP請求。而HTTP請求耗時是web性能的最大的影響之一。

    3. 腳本延遲加載

    HTML 4為<script>标簽定義了一個擴充屬性:defer。defer 屬性規定當頁面已完成加載後,才會執行腳本。目前所有主流浏覽器都支援defer。注意:defer 屬性僅适用于外部腳本(隻有在使用 src 屬性時)。

    一個帶有defer屬性的<script>标簽可以放置在文檔的任何位置,它會在被解析時啟動下載下傳,直到DOM加載完成(在onload事件句柄被調用之前)。當一個defer的Javascript檔案被下載下傳時,它不會阻塞浏覽器的其他處理過程,是以這些檔案可以與其他資源一起并行下載下傳。

   除了defer屬性外,HTML 5 規範中引入了async屬性,用于異步加載腳本。async與defer的相同點是采用并行下載下傳,在下載下傳過程中不會産生阻塞。差別在于執行時機,async是加載完成後自動執行,而defer需要等待頁面完成後執行。這就會造成腳本的執行順序和頁面上腳本的排放順序不一緻,可能造成腳本依賴的問題(指async)。

腳本無阻塞加載政策

   4. 動态腳本加載

   DOM允許我們使用Javascript動态建立HTML的幾乎所有文檔内容,一個新的<script>元素可以非常容易的通過标準DOM建立:

var script=document.createElemetn("script");
 
  script.type="text/javascript";
 
  script.src="file.js";;
 
  document.getElementsByTagName("head").appendChild(script);
           

    新的<script>元素加載file1.js源檔案。此檔案當元素添加到頁面後立刻開始下載下傳。此技術的重點在于:無論在何處啟動下載下傳,檔案的下載下傳和運作都不會阻塞其他頁面處理過程。

    當檔案使用動态腳本節點下載下傳時,傳回的代碼通常立即執行(除了Firefox和Opera,它們将等待此前的所有動态腳本節點執行完畢)。

    大多數情況下,我們希望調用一個函數就可以實作Javascript檔案的動态下載下傳。下面的函數封裝實作了标準實作和IE實作:

function loadScript(url, callback){  
    var script = document.createElement ("script") ;  
   script.type = "text/javascript";  
       
    if (script.readyState){ //IE  
       script.onreadystatechange = function(){  
         if (script.readyState == "loaded" || script.readyState == "complete"){  
           script.onreadystatechange = null;  
           callback();   
          }  
       };  
     }   
     else { //Others  
       script.onload = function(){ callback();  
     };   
   }  
   script.src = url;  
   document.getElementsByTagName("head")[0].appendChild(script);   
 }  
 
loadScript("file1.js", function(){  //調用  
    alert("File is loaded!");   
}); 
           

    如果需要加載多個js檔案,一定要考慮清楚檔案的加載順序。你可以将下載下傳操作串聯起來以確定下載下傳順序:

loadScript("file1.js",function(){
      loadScript("file2.js",function(){
         loadScript("file3.js",function(){
          alert("ok");
          });
      }); 
 });
           

    此函數接受兩個參數:Javascript檔案的Url和一個當Javascript接收完成時觸發的回調函數。屬性檢查用于決定監視哪種事件。最後一步src屬性,并将javascript檔案添加到head。

    動态腳本加載憑借它在跨浏覽器相容性和易用的優勢,成為最通用的無阻塞加載解決方案。

繼續閱讀