標籤:blog code java c http get
《高效能網站建設進階指南》
指令碼如果按照常規方式載入,不僅會阻塞頁面中其他內容的下載,還會阻塞指令碼後面所有元素的渲染。非同步載入指令碼可以避免這種阻塞現象,從而提高頁面載入速度。但是效能的提升是要付出代價的。代碼的非同步執行可能會出現競爭狀態。簡單地說就是頁面內部的指令碼需要的標示符如果是在外部檔案中定義的,而當外部檔案非同步載入的時候,如果沒有保證外部檔案和內部指令碼執行順序,很有可能會出現未定義標示符的錯誤
當非同步載入的外部指令碼與行內指令碼之間存在代碼依賴時,就需要通過一種保證執行順序的方法來整合這兩個指令碼。
如何保證執行順序
當外部指令碼按常規方式載入時,他會阻塞行內代碼的執行,不會出現因為競爭狀態而導致的未定義標示符錯誤。有幾個技術可以協助我們保證執行順序。
- 寫入程式碼回調 (Hardcoded Callback)
- Window Onlad
- 定時器 (Timer)
- Degrading Script Tags
方法1:寫入程式碼回調 (Hardcoded Callback)
讓外部的指令碼調用內部指令碼的函數,以確保代碼的順序執行。例如link
//行內代碼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 pageinit();
如果開發人員可以同時控制首頁面和外部指令碼,這種技術是可行的的。但是我們常常會調用第三方的 JavaScript ,比如: jQuery ,我們不可能降回調添加在 jQuery 的檔案中。而且這種方法也不太靈活,一旦改變了回呼函數需要同時修改外部指令碼。
方法2: Window Onload
通過監聽 Window 的 onload 事件來觸發行內代碼的執行。這使得只要確保外部指令碼在 window.onload 之前下載執行就能保證執行順序。有些非同步載入技術確保在 window.onload 觸發之前載入外部腳步:
使用其中一種技術,再通過 window.onload 觸發行內指令碼就可以實現並行下載的同時保證執行順序。查看官網demo。這個例子使用了 Script in Iframe 方法載入外部指令碼,幾乎在所有的瀏覽器中它都會阻塞onload事件。外部指令碼被嵌入在 menu.php 中,然後用 iframe 載入它而不是直接載入 menu.js 。依據瀏覽器的差異選用 addEventListener 或者 attachEvent 比簡單的地使用 window.onload() 好一些。關於window.onload載入的多種解決方案 ?
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 是整合非同步載入外部指令碼和行內指令碼的首選。不引用任何外部的標示符,所以維護簡單。行內指令碼可以在外部指令碼載入之後立即執行。同時事件處理也很簡單
……未完待續……