從上圖可以看出,當瀏覽器遇到<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。
動態腳本加載憑藉它在跨瀏覽器兼容性和易用的優勢,成爲最通用的無阻塞加載解決方案。