高性能網站優化-確保異步加載腳本時保持執行順序

《高性能網站建設進階指南》javascript

腳本若是按照常規方式加載,不只會阻塞頁面中其餘內容的下載,還會阻塞腳本後面全部元素的渲染。異步加載腳本能夠避免這種阻塞現象,從而提升頁面加載速度。可是性能的提高是要付出代價的。代碼的異步執行可能會出現競爭狀態。簡單地說就是頁面內部的腳本須要的標示符若是是在外部文件中定義的,而當外部文件異步加載的時候,若是沒有保證外部文件和內部腳本執行順序,頗有可能會出現未定義標示符的錯誤php

當異步加載的外部腳本與行內腳本之間存在代碼依賴時,就須要經過一種保證執行順序的方法來整合這兩個腳本。css

如何保證執行順序

當外部腳本按常規方式加載時,他會阻塞行內代碼的執行,不會出現由於競爭狀態而致使的未定義標示符錯誤。有幾個技術能夠幫助咱們保證執行順序。html

  • 硬編碼回調 (Hardcoded Callback)
  • Window Onlad
  • 定時器 (Timer)
  • Degrading Script Tags

方法1:硬編碼回調 (Hardcoded Callback)

讓外部的腳本調用內部腳本的函數,以確保代碼的順序執行。例如linkjava

//行內代碼
function init() {
  createMenu('examples');
}
var domscript = document.createElement('script');
domscript.src = "menu-with-init.js";
document.getElementsByTagName('head')[0].appendChild(domscript);

//外部文件
function createMenu(id) {
  [...]
}
// callback to the main page
init();

若是開發人員能夠同時控制主頁面和外部腳本,這種技術是可行的的。可是咱們經常會調用第三方的 JavaScript ,好比: jQuery ,咱們不可能降回調添加在 jQuery 的文件中。並且這種方法也不太靈活,一旦改變了回調函數須要同時修改外部腳本。git

方法2: Window Onload

經過監聽 Window 的 onload 事件來觸發行內代碼的執行。這使得只要確保外部腳本在 window.onload 以前下載執行就能保證執行順序。有些異步加載技術確保在 window.onload 觸發以前加載外部腳步:github

  • Script in Iframe 在IE、Firefox、Safari、Chrome 和 Opera 中保持順序執行瀏覽器

  • Script DOM 在Firefox、Safari 和 Chrome 中保持順序執行app

使用其中一種技術,再經過 window.onload 觸發行內腳本就能夠實現並行下載的同時保證執行順序。查看官網demo。這個例子使用了 Script in Iframe 方法加載外部腳本,幾乎在全部的瀏覽器中它都會阻塞onload事件。外部腳本被嵌入在 menu.php 中,而後用 iframe 加載它而不是直接加載 menu.js 。依據瀏覽器的差別選用 addEventListener 或者 attachEvent 比簡單的地使用 window.onload() 好一些。關於window.onload加載的多種解決方案 »dom

Window Onload 整合技術有兩個缺點:首先,必須肯定異步腳本是經過阻塞 onload 事件的方式加載的。其次,可能會形成行內代碼的延遲執行。若是頁面還有不少其餘的資源,好比圖片等,那麼外部腳本加載執行結束以後, window.onload內部的代碼必須等到頁面徹底加載以後纔可以執行。一般行內腳本最好在外部腳本下載和執行以後當即調用。

方法3:定時器(Timer)

定時器技術指的是使用輪詢方法來保證在行內代碼執行之,前所依賴的外部腳本已經加載。《高性能網站建設進階優化》一書給出的demo中能夠看到link。修改行內代碼,添加一個新函數 initTimer ,負責檢查依賴的命名空間和標示符是否存在。若是存在,則調用須要調用的函數;若是不存在,就在指定的時間段以後再次調用 initTimer 函數檢查命名空間和標示符。

function initTimer() {
    if ( "undefined" === typeof(EFWS) ) {
        setTimeout(initTimer, 300);
    }
    else {
        init();
    }
}

這個技術也有它的缺點。若是setTimeout方法中設置的事件間隔過小,可能會增長頁面的開銷。相反,若是設置太大,又可能形成外部腳本加載完成和行內代碼開始執行之間的延遲。就上面的例子來講,若是外部腳本加載失敗,即行內腳本永遠沒法檢測到指定的命名空間,輪詢將會無限進行下去。同時稍微增長了維護的成本,若是外部文件的命名空間和標示符變了,行內代碼也要更新。

方法4:Script Onload

前面的那些整合技術會增長頁面的脆弱性,開銷,致使頁面的延遲。Script Onload 方法經過監聽腳本的 onload 事件解決了全部的這些問題。link。考慮到瀏覽器之間的差別,添加了 script 元素的 onload 和 onreadystatechange 事件處理程序。onload 在其餘瀏覽器中有效,Opera 二者都有效。

var DOMScript=document.createElement("script");
DOMScript.src="someting.js";
DOMScript.onloadDone=false;
DOMScript.onload=function(){
    DOMScript.onloadDone=true;
    init();
}
DOMScript.onreadystatechange=function(){
    if(("loaded" === DOMScript.readyState || "complete" === DOMScript.readyState) && ! DOMScript.onloadDone){
        DOMScript.onloadDone=true;
        init();
    }
}

Script Onload 是整合異步加載外部腳本和行內腳本的首選。不引用任何外部的標示符,因此維護簡單。行內腳本能夠在外部腳本加載以後當即執行。同時事件處理也很簡單

……未完待續……

相關文章
相關標籤/搜索