jquery 的到來使得 javascript 的編寫過程變得異常簡單。但是,您會注意到對代碼進行小的更改可以顯著提高可讀性和/或性能。以下是一些可幫助您優化代碼的提示。
設置平臺
我們需要一個可靠的平臺來進行測試。以下是測試頁面的 HTML 標記,我們將在其中運行所有測試:
? <title>Testing out performance enhancements - Siddharth/NetTuts+</title><div id="container"> <div class="block"> <p id="first"> Some text here </p> <ul id="someList"> <li class="item"> <li class="item selected" id="mainItem">Oh, hello there!</li> <li class="item"> <li class="item"> </ul> </div> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script><script> console.profile() ; // Our code here console.profileEnd(); </script>
這里沒有什么特別的;只是我們可以定位和測試的一堆元素。我們使用 Firebug 來記錄此處的時間。 profile 開始該過程,profileEnd 停止該過程,并記錄該任務花費的時間。我通常使用 Firebug 的 main profile 方法,但對于我們的不正當目的,這就足夠了。
1.檢測元素是否存在
通常情況下,您將向站點中的所有頁面提供包含代碼的單個腳本文件。這通常是經常對當前頁面中不存在的元素執行操作的代碼。盡管 jQuery 非常優雅地處理此類問題,但這并不意味著您可以忽略任何問題。事實上,如果您在空集合上調用 jQuery 的方法,它們將不會運行。
作為最佳實踐,僅運行適用于當前加載頁面的代碼,而不是將所有代碼集中到單個文檔就緒檢查中,并將其提供給客戶端。
讓我們看看第一個場景:
console.profile(); var ele = $("#somethingThatisNotHere"); ele.text("Some text").slideUp(300).addClass("editing"); $("#mainItem"); console.profileEnd(); //Some more awesome, ground shattering code here ._.
Firebug 輸出以下結果:
這一次,我們在執行操作之前先檢查一下要執行操作的元素是否存在。
console.profile() ; var ele = $("#somethingThatisNotHere"); if ( ele[0] ) { ele.text("Some text").slideUp(300).addClass("editing"); } $("#mainItem"); console.profileEnd(); //Some more awesome, ground shattering code here ._.
結果:
看到了嗎?這非常簡單,切中要點并完成工作。 請注意,您無需檢查代碼的每一位是否都存在元素。您會在頁面中注意到某些較大的部分通常會從此方法中受益。在這里運用你的判斷。
2.有效使用選擇器
嘗試使用 ID 而不是傳遞類。
這是一個很大的主題,所以我會盡可能簡潔。首先,在傳遞選擇器時,嘗試使用 ID 而不是傳遞類。 jQuery 直接使用本機 getElementById 方法通過 ID 查找元素,而對于類,它必須執行一些內部巫術才能獲取它,至少在舊版瀏覽器中是這樣。
我們將了解可用于定位第二個 li 元素的不同選擇器。我們將測試它們中的每一個以及它們如何改變性能。
第一種方法,也是最簡單的方法,是使用 selected 類明確地定位它。讓我們看看 Firebug 的探查器返回什么。
console.profile() ; $(".selected"); console.profileEnd();
結果:0.308ms。接下來,我們為標簽名稱添加前綴以縮小范圍。這樣,我們可以通過首先使用 document.getElementsByTagName 僅定位選定的 DOM 元素來縮小搜索范圍。
console.profile() ; $("li.selected"); console.profileEnd();
結果:0.291ms。大約縮短了 0.02 毫秒。由于我們在 Firefox 中進行測試,這一點可以忽略不計;但是,應該指出的是,這種性能提升在較舊的瀏覽器(例如 Internet Explorer 6)中會明顯更高。
接下來,我們從父元素的 ID 開始下降。
console.profile() ; $("#someList .selected"); console.profileEnd();
結果:0.283ms。讓我們嘗試更具體一點。除了祖先的 ID 之外,我們還指定元素的類型。
console.profile() ; $("#someList li.selected"); console.profileEnd();
結果:0.275 毫秒。還有一小部分被剃掉了。最后,我們直接使用 ID 來定位它。
console.profile() ; $("#mainItem"); console.profileEnd();
結果:0.165ms。感人的!這確實向您展示了運行本機方法的速度有多快。請注意,雖然現代瀏覽器可以利用 getElementsByClassName 之類的功能,但舊版瀏覽器卻不能 – 導致性能大大降低。編碼時始終考慮這一點。
3. 解釋 Sizzle 的解析模型并添加范圍
Sizzle,jQuery 使用的選擇器引擎 – 由 John Resig 構建 – 從右到左解析選擇器,這會引發一些意想不到的解析鏈。
考慮這個選擇器:
$("#someList .selected");
當Sizzle遇到這樣的選擇器時,它首先構建DOM結構,使用選擇器作為根,丟棄不具有所需類的項目,并且對于具有該類的每個元素,它檢查其父元素是否具有ID 為 someList。
為了解決這個問題,請確保選擇器最右側的部分盡可能具體。例如,通過指定 li.selected 而不是 .selected,您可以減少必須檢查的節點數量。這就是上一節中性能跳躍的原因。通過添加額外的約束,您可以有效地減少必須檢查的節點數量。
為了更好地調整元素的獲取方式,您應該考慮為每個請求添加上下文。
var someList = $('#someList')[0]; $(".selected", someList);
通過添加上下文,搜索元素的方式完全改變?,F在,首先搜索提供上下文的元素(在我們的示例中為 someList),一旦獲得該元素,就會刪除不具有必需類的子元素。
請注意,將 DOM 元素作為 jQuery 選擇器的上下文傳遞通常是最佳實踐。當上下文存儲在某個變量中時,使用上下文是最有幫助的。否則,您可以簡化該過程并使用 find() —— jQuery 本身就是在幕后做的。
$('#someList').find('.selected');
我想說性能提升將會被明確定義,但我不能。我已經在許多瀏覽器上運行了測試,范圍方法的性能是否優于普通版本取決于許多因素,包括瀏覽器是否支持特定方法。
4.避免查詢浪費
當您瀏覽別人的代碼時,您經常會發現。
// Other code $(element).doSomething(); // More code $(element).doSomethingElse(); // Even more code $(element).doMoreofSomethingElse();
請不要這樣做。 永遠。開發人員一遍又一遍地實例化這個“元素”。這是浪費。
讓我們看看運行這樣可怕的代碼需要多長時間。
console.profile() ; $("#mainItem").hide(); $("#mainItem").val("Hello"); $("#mainItem").html("Oh, hey there!"); $("#mainItem").show(); console.profileEnd();
如果代碼的結構如上所示,一個接一個,您可以像這樣使用鏈接:
console.profile(); $("#mainItem").hide().val("Hello").html("Oh, hey there!").show(); console.profileEnd();
通過鏈接,獲取最初傳入的元素,并將引用傳遞給每個后續調用,從而減少執行時間。否則每次都會創建一個新的 jQuery 對象。
但是,如果與上面不同,引用該元素的部分不是并發的,則您必須緩存該元素,然后執行與以前相同的所有操作。
console.profile() ; var elem = $("#mainItem"); elem.hide(); //Some code elem.val("Hello"); //More code elem.html("Oh, hey there!"); //Even more code elem.show(); console.profileEnd();
從結果中可以明顯看出,緩存或鏈接大大減少了執行時間。
5.更智能地執行 DOM 操作
我之前的文章中建議進行非傳統 DOM 操作,但在被證明性能提升確實值得之前,遭到了一些人的批評。我們現在將親自測試一下。
對于測試,我們將創建 50 個 li 元素,并將它們附加到當前列表,并確定需要多少時間。
我們將首先回顧一下正常的、低效的方法。我們實質上是在每次循環運行時將元素附加到列表中。
console.profile() ; var list = $("#someList"); for (var i=0; iItem #' + i + ''); } console.profileEnd();
讓我們看看效果如何,好嗎?
現在,我們將走一條稍微不同的道路。我們首先會將所需的 HTML 字符串附加到變量中,然后僅回流 DOM 一次。
console.profile() ; var list = $("#someList"); var items = ""; for (var i=0; iItem #' + i + ''; } list.append(items); console.profileEnd();
正如預期的那樣,所花費的時間顯著減少。
如果您使用 jQuery 作為 getElementById 的替代品,但從未使用它提供的任何方法,那么您就做錯了。
如果您想更進一步,問問自己是否真的需要創建一個新的 jQuery 對象來定位某些元素?如果您使用 jQuery 作為 document.getElementById 的替代品,但從未使用它提供的任何方法,那么您就做錯了。在這種情況下,我們可以使用原始 JS。
console.profile() ; var list = document.getElementById('someList'); var items = ''; for (var i=0; iItem #' + i + ''; } list.innerHTML = items; console.profileEnd();
一些注意事項
您會注意到優化代碼和未優化代碼之間的執行時間差異只有幾分之一毫秒。這是因為我們的測試文檔非常小,節點數量少得令人難以置信。一旦您開始使用包含數千個節點的生產級站點,它就會真正增加。
另請注意,在大多數測試中,我只是訪問元素。當您開始對它們應用適當的函數時,執行時間的增量將會增加。
我也明白這不是測試性能的最科學的方法,但是,為了大致了解這些變化對性能的影響程度,我認為這已經足夠了。
最后,在大多數網絡應用程序中,相關網絡服務器的連接速度和響應時間對應用程序性能的影響比對代碼的調整影響更大。盡管如此,這仍然是重要的信息,并且當您試圖從代碼中獲得盡可能多的性能時,它將幫助您。
這就是大家
我們就完成了。當您嘗試優化代碼時需要記住以下幾點;當然,這并不是所有的調整列表,并且這些要點不一定適用于所有情況。無論哪種方式,我都會密切關注評論,以閱讀您對這個主題的看法。你看到這里有什么錯誤嗎?請在下面給我留言。
有疑問嗎?好話要說嗎?批評?點擊評論部分并給我留言??鞓肪幋a!