js最大的問題是:不管當前JavaScript代碼是內嵌仍是在外鏈文件中,頁面的下載和渲染都必須停下來等待腳本執行完成。JavaScript執行過程耗時越久,瀏覽器等待響應用戶輸入的時間就越長。瀏覽器在下載和執行腳本時出現阻塞.javascript
解決辦法:css
一:腳本的位置:html
因爲腳本會阻塞頁面其餘資源的下載,所以推薦將全部<script>標籤儘量放到<body>標籤的底部,以儘可能減小對整個頁面下載的影響。java
例如:瀏覽器
<html> <head> <title>Source Example</title> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <p>Hello world!</p> <!-- Example of efficient script positioning --> <script type="text/javascript" src="script1.js"></script> <script type="text/javascript" src="script2.js"></script> <script type="text/javascript" src="script3.js"></script> </body> </html>
二: 組織腳本 緩存
因爲每一個<script>標籤初始下載時都會阻塞頁面渲染,因此減小頁面包含的<script>標籤數量有助於改善這一狀況。這不只針對外鏈腳本,內嵌腳本的數量一樣也要限制。瀏覽器在解析 HTML 頁面的過程當中每遇到一個<script>標籤,都會因執行腳本而致使必定的延時,所以最小化延遲時間將會明顯改善頁面的整體性能。安全
這個問題在處理外鏈 JavaScript 文件時略有不一樣。考慮到 HTTP 請求會帶來額外的性能開銷,所以下載單個 100Kb 的文件將比下載 5 個 20Kb 的文件更快。也就是說,減小頁面中外鏈腳本的數量將會改善性能。服務器
因此儘可能合併和壓縮js網絡
三:延遲加載腳本app
HTML 4 爲<script>標籤訂義了一個擴展屬性:defer。Defer 屬性指明本元素所含的腳本不會修改 DOM,所以代碼能安全地延遲執行
用法:
<script type="text/javascript" src="script1.js" defer></script>
HTML 5 爲<script> 標籤訂義了一個新的擴展屬性:async 。它的做用和 defer 同樣,可以異步地加載和執行腳本,不由於加載腳本而阻塞頁面的加載。可是有一點須要注意,在有 async 的狀況下,JavaScript 腳本一旦下載好了就會執行,因此頗有可能不是按照本來的順序來執行的。若是 JavaScript 腳本先後有依賴性,使用 async 就頗有可能出現錯誤。
用法:
<script type="text/javascript" src="script1.js" async></script>
四:動態腳本元素
文檔對象模型(DOM)容許您使用 JavaScript 動態建立 HTML 的幾乎所有文檔內容。<script>元素與頁面其餘元素同樣,能夠很是容易地經過標準 DOM 函數建立:
清單 6 經過標準 DOM 函數建立<script>元素
Js代碼
var script = document.createElement ("script");
script.type = "text/javascript";
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);
新的<script>元素加載 script1.js 源文件。此文件當元素添加到頁面以後馬上開始下載。此技術的重點在於:不管在何處啓動下載,文件的下載和運行都不會阻塞其餘頁面處理過程。您甚至能夠將這些代碼放在<head>部分而不會對其他部分的頁面代碼形成影響(除了用於下載文件的 HTTP 鏈接)。
當文件使用動態腳本節點下載時,返回的代碼一般當即執行(除了 Firefox 和 Opera,他們將等待此前的全部動態腳本節點執行完畢)。當腳本是「自運行」類型時,這一機制運行正常,可是若是腳本只包含供頁面其餘腳本調用調用的接口,則會帶來問題。這種狀況下,您須要跟蹤腳本下載完成並是否準備妥善。可使用動態 <script> 節點發出事件獲得相關信息。
Firefox、Opera, Chorme 和 Safari 3+會在<script>節點接收完成以後發出一個 onload 事件。您能夠監聽這一事件,以獲得腳本準備好的通知:
清單 7 經過監聽 onload 事件加載 JavaScript 腳本
Js代碼
var script = document.createElement ("script")
script.type = "text/javascript";
//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
alert("Script loaded!");
};
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);
Internet Explorer 支持另外一種實現方式,它發出一個 readystatechange 事件。<script>元素有一個readyState 屬性,它的值隨着下載外部文件的過程而改變。readyState 有五種取值:
「uninitialized」:默認狀態
「loading」:下載開始
「loaded」:下載完成
「interactive」:下載完成但尚不可用
「complete」:全部數據已經準備好
微軟文檔上說,在<script>元素的生命週期中,readyState 的這些取值不必定所有出現,但並無指出哪些取值總會被用到。實踐中,咱們最感興趣的是「loaded」和「complete」狀態。Internet Explorer 對這兩個 readyState 值所表示的最終狀態並不一致,有時<script>元素會獲得「loader」卻從不出現「complete」,但另一些狀況下出現「complete」而用不到「loaded」。最安全的辦法就是在readystatechange 事件中檢查這兩種狀態,而且當其中一種狀態出現時,刪除readystatechange事件句柄(保證事件不會被處理兩次):
清單 8 經過檢查readyState狀態加載JavaScript腳本
Js代碼
var script = document.createElement("script")
script.type = "text/javascript";
//Internet Explorer
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
alert("Script loaded.");
}
};
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);
大多數狀況下,您但願調用一個函數就能夠實現JavaScript文件的動態加載。下面的函數封裝了標準實現和 IE 實現所需的功能:
清單 9 經過函數進行封裝
Js代碼
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);
}
此函數接收兩個參數:JavaScript 文件的 URL,和一個當 JavaScript 接收完成時觸發的回調函數。屬性檢查用於決定監視哪一種事件。最後一步,設置 src 屬性,並將<script>元素添加至頁面。此loadScript() 函數使用方法以下:
清單 10 loadScript()函數使用方法
Js代碼
loadScript("script1.js", function(){
alert("File is loaded!");
});
您能夠在頁面中動態加載不少 JavaScript 文件,但要注意,瀏覽器不保證文件加載的順序。全部主流瀏覽器之中,只有 Firefox 和 Opera 保證腳本按照您指定的順序執行。其餘瀏覽器將按照服務器返回它們的次序下載並運行不一樣的代碼文件。您能夠將下載操做串聯在一塊兒以保證他們的次序,以下:
清單 11 經過 loadScript()函數加載多個JavaScript腳本
Js代碼
loadScript("script1.js", function(){
loadScript("script2.js", function(){
loadScript("script3.js", function(){
alert("All files are loaded!");
});
});
});
此代碼等待 script1.js 可用以後纔開始加載 script2.js,等 script2.js 可用以後纔開始加載 script3.js。雖然此方法可行,但若是要下載和執行的文件不少,仍是有些麻煩。若是多個文件的次序十分重要,更好的辦法是將這些文件按照正確的次序鏈接成一個文件。獨立文件能夠一次性下載全部代碼(因爲這是異步進行的,使用一個大文件並無什麼損失)。
動態腳本加載是非阻塞 JavaScript 下載中最經常使用的模式,由於它能夠跨瀏覽器,並且簡單易用。
使用XMLHttpRequest(XHR)對象
此技術首先建立一個 XHR 對象,而後下載 JavaScript 文件,接着用一個動態 <script> 元素將 JavaScript 代碼注入頁面。清單 12 是一個簡單的例子:
清單 12 經過 XHR 對象加載 JavaScript 腳本
Js代碼
var xhr = new XMLHttpRequest();
xhr.open("get", "script1.js", true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
var script = document.createElement ("script");
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
};
xhr.send(null);
此代碼向服務器發送一個獲取 script1.js 文件的 GET 請求。onreadystatechange 事件處理函數檢查readyState 是否是 4,而後檢查 HTTP 狀態碼是否是有效(2XX 表示有效的迴應,304 表示一個緩存響應)。若是收到了一個有效的響應,那麼就建立一個新的<script>元素,將它的文本屬性設置爲從服務器接收到的 responseText 字符串。這樣作實際上會建立一個帶有內聯代碼的<script>元素。一旦新<script>元素被添加到文檔,代碼將被執行,並準備使用。
這種方法的主要優勢是,您能夠下載不當即執行的 JavaScript 代碼。因爲代碼返回在<script>標籤以外(換句話說不受<script>標籤約束),它下載後不會自動執行,這使得您能夠推遲執行,直到一切都準備好了。另外一個優勢是,一樣的代碼在全部現代瀏覽器中都不會引起異常。
此方法最主要的限制是:JavaScript 文件必須與頁面放置在同一個域內,不能從 CDN 下載(CDN 指」內容投遞網絡(Content Delivery Network)」,因此大型網頁一般不採用 XHR 腳本注入技術。
總結
減小 JavaScript 對性能的影響有如下幾種方法:
將全部的<script>標籤放到頁面底部,也就是</body>閉合標籤以前,這能確保在腳本執行前頁面已經完成了渲染。
儘量地合併腳本。頁面中的<script>標籤越少,加載也就越快,響應也越迅速。不管是外鏈腳本仍是內嵌腳本都是如此。
採用無阻塞下載 JavaScript 腳本的方法:
使用<script>標籤的 defer 屬性(僅適用於 IE 和 Firefox 3.5 以上版本);
使用動態建立的<script>元素來下載並執行代碼;
使用 XHR 對象下載 JavaScript 代碼並注入頁面中。
經過以上策略,能夠在很大程度上提升那些須要使用大量 JavaScript 的 Web 網站和應用的實際性能。
相關網址:
http://www.cnblogs.com/snandy/archive/2011/04/26/2029537.html
http://www.cnblogs.com/_franky/archive/2010/06/20/1761370.html
http://www.360doc.com/content/15/0208/17/9200790_447256181.shtml