加載外部 JavaScript 的最佳方式
不久前,我寫了一篇關於通過創建動態 <script>
加載 JavaScript 而不會阻塞的文章 標籤。當 <script>
標籤位於 HTML 文檔流中,瀏覽器必須停止渲染並等待腳本文件下載並執行,然後才能繼續(示例)。創建一個新的 <script>
通過 JavaScript 標記避免了這個問題,因為它超出了文檔的流程,因此腳本文件無需等待即可下載並執行。結果:動態加載 JavaScript 文件可以讓您的頁面更快地呈現,從而提高感知性能。
最好的技術
Steve Souders 在他的博客和他的書中探索了幾種不同的方式來加載 JavaScript 而不會阻塞。經過思考和實驗,我得出的結論是,在不阻塞的情況下加載 JavaScript 的最佳實踐只有一種:
- 創建兩個 JavaScript 文件。第一個僅包含動態加載 JavaScript 所需的代碼,第二個包含頁面上初始交互級別所需的所有其他內容。
- 使用
<script>
包含第一個 JavaScript 文件 頁面底部的標籤,就在</body>
內 . - 創建第二個
<script>
調用該函數以加載第二個 JavaScript 文件並包含任何其他初始化代碼的標記。
而已!真的沒有必要做任何其他事情。關鍵點是只有兩個 JavaScript 並使第一個盡可能小。例如,第一個文件可能只包含這個函數:
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);
}
只需少量代碼即可啟動您的引導程序,因此加載速度非常快(尤其是在 gzip 壓縮時)。
您頁面上的實際代碼最終看起來像這樣:
<script type="text/javascript" src="http://your.cdn.com/first.js"></script>
<script type="text/javascript">
loadScript("http://your.cdn.com/second.js", function(){
//initialization code
});
</script>
整個技術的關鍵是只有兩個 JavaScript 文件,所以第二個包含初始化頁面所需的所有內容。如果您的頁面需要兩個以上的文件怎麼辦?然後,您應該在構建時(使用 Sprockets 之類的東西)或運行時(使用 mod_concat 或組合處理程序之類的東西)將文件連接在一起。任何時候您的頁面都不應該需要超過這兩個 JavaScript 文件才能正確初始化。每個額外的 HTTP 請求都有開銷,然後您需要擔心下載順序,以便以正確的順序執行代碼。通過只有兩個文件,您無需擔心首先下載和執行哪個文件,同時也消除了不必要的 HTTP 請求。
腳本放置
你會注意到我提到了將這段代碼放在頁面末尾的最佳實踐,就在結束的 </body>
內 標籤。這是已經存在了一段時間的建議,我仍然推薦它,即使在使用這種技術時也是如此。原因是您可以保證頁面上已經存在您可能需要的所有 DOM 元素。提前加載腳本可能會引入時間問題,您需要擔心使用 window.onload
或其他方法來確定 DOM 何時可以使用。通過在頁面底部包含此代碼,您可以確保 DOM 已準備好被戳,您無需再延遲初始化。
內聯第一個腳本
一些評論者正確地指出,可以通過將初始函數內聯而不是將其保存在外部文件中來進一步優化該技術。通常,出於可維護性目的,我喜歡將 JavaScript 保留在頁面代碼之外。我還預計由於某種原因,頁面上的初始 JavaScript 代碼將比僅此函數大。如果您可以通過某種自動化將其作為內聯腳本注入您的頁面,我完全贊成!關鍵是要確保腳本足夠小,使其運行時性能不會影響頁面加載。
YUI 3 為您服務
YUI 3 就是圍繞這個前提設計的。您可以從加載 yui.js 文件開始,然後使用內置的 Loader 組件動態加載 YUI 庫的其餘部分。例如:
<script src="http://yui.yahooapis.com/3.0.0b1/build/yui/yui-min.js"
type="text/javascript"></script>
<script type="text/javascript">
YUI().use("node", function(Y){
//initialization code
});
</script>
此代碼首先加載到 YUI “種子”文件中,然後創建 YUI
的新實例 對象並指示“節點”組件是必要的。在幕後,YUI 為“node”構建了一個包含所有依賴項的 URL,動態加載它,然後在完成時調用回調函數。 YUI 3 方法的一個很酷的地方是,您無需擔心靜態包含 JavaScript 的 URL,只需指出您需要哪些組件,然後庫會計算出正確的下載 URL(詳情)。
結論
儘管已經有很多關於無阻塞加載 JavaScript 的方法的研究,但我確實只推荐一種方法作為最佳實踐。真的應該不需要加載超過兩個腳本來讓您的站點初始化和交互。使初始 JavaScript 文件盡可能小,然後動態加載較大的文件以避免阻塞。這是在不影響用戶體驗的情況下將所有 JavaScript 放到頁面上的最簡單、最簡單的方法。
更新(2009 年 8 月 1 日): 添加了關於腳本放置的部分,以闡明我為什麼在 <body>
中這樣做 而不是 <head>
.