jQuery 的到來使得 JavaScript 的編寫過程變得異常簡單。但是,您會注意到對程式碼進行小的更改可以顯著提高可讀性和/或效能。以下是一些可協助您優化程式碼的提示。
我們需要一個可靠的平台來測試。以下是測試頁面的 HTML 標記,我們將在其中執行所有測試:
<!DOCTYPE html> <html lang="en-GB"> <head> <title>Testing out performance enhancements - Siddharth/NetTuts+</title> </head> <body> <div id="container"> <div class="block"> <p id="first"> Some text here </p> <ul id="someList"> <li class="item"></li> <li class="item selected" id="mainItem">Oh, hello there!</li> <li class="item"></li> <li class="item"></li> </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> </body> </html>
這裡沒有什麼特別的;只是我們可以定位和測試的一堆元素。我們使用 Firebug 來記錄這裡的時間。 profile 開始這個過程,profileEnd 停止這個過程,並記錄該任務所花費的時間。我通常使用 Firebug 的 main profile 方法,但對於我們的不正當目的,這就足夠了。
通常情況下,您將向網站中的所有頁面提供包含程式碼的單一腳本檔案。這通常是經常對目前頁面中不存在的元素執行操作的程式碼。儘管 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 ._.
結果:
看到了嗎?這非常簡單,切中要點並完成工作。 請注意,您無需檢查程式碼的每一位是否都存在元素。您會在頁面中註意到某些較大的部分通常會從此方法中受益。在這裡運用你的判斷。
嘗試使用 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 之類的功能,但舊版瀏覽器卻不能 - 導致效能大大降低。編碼時始終考慮這一點。
Sizzle,jQuery 使用的選擇器引擎 - 由 John Resig 建立 - 從右到左解析選擇器,這會引發一些意想不到的解析鏈。
考慮這個選擇器:
$("#someList .selected");
当Sizzle遇到这样的选择器时,它首先构建DOM结构,使用选择器作为根,丢弃不具有所需类的项目,并且对于具有该类的每个元素,它检查其父元素是否具有ID 为 someList。
为了解决这个问题,请确保选择器最右侧的部分尽可能具体。例如,通过指定 li.selected 而不是 .selected,您可以减少必须检查的节点数量。这就是上一节中性能跳跃的原因。通过添加额外的约束,您可以有效地减少必须检查的节点数量。
为了更好地调整元素的获取方式,您应该考虑为每个请求添加上下文。
var someList = $('#someList')[0]; $(".selected", someList);
通过添加上下文,搜索元素的方式完全改变。现在,首先搜索提供上下文的元素(在我们的示例中为 someList),一旦获得该元素,就会删除不具有必需类的子元素。
请注意,将 DOM 元素作为 jQuery 选择器的上下文传递通常是最佳实践。当上下文存储在某个变量中时,使用上下文是最有帮助的。否则,您可以简化该过程并使用 find() —— jQuery 本身就是在幕后做的。
$('#someList').find('.selected');
我想说性能提升将会被明确定义,但我不能。我已经在许多浏览器上运行了测试,范围方法的性能是否优于普通版本取决于许多因素,包括浏览器是否支持特定方法。
当您浏览别人的代码时,您经常会发现。
// 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();
从结果中可以明显看出,缓存或链接大大减少了执行时间。
我之前的文章中建议进行非传统 DOM 操作,但在被证明性能提升确实值得之前,遭到了一些人的批评。我们现在将亲自测试一下。
对于测试,我们将创建 50 个 li 元素,并将它们附加到当前列表,并确定需要多少时间。
我们将首先回顾一下正常的、低效的方法。我们实质上是在每次循环运行时将元素附加到列表中。
console.profile() ; var list = $("#someList"); for (var i=0; i<50; i++) { list.append('<li>Item #' + i + '</li>'); } console.profileEnd();
让我们看看效果如何,好吗?
现在,我们将走一条稍微不同的道路。我们首先会将所需的 HTML 字符串附加到变量中,然后仅回流 DOM 一次。
console.profile() ; var list = $("#someList"); var items = ""; for (var i=0; i<50; i++){ items += '<li>Item #' + i + '</li>'; } 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; i<50; i++){ items += '<li>Item #' + i + '</li>'; } list.innerHTML = items; console.profileEnd();
您会注意到优化代码和未优化代码之间的执行时间差异只有几分之一毫秒。这是因为我们的测试文档非常小,节点数量少得令人难以置信。一旦您开始使用包含数千个节点的生产级站点,它就会真正增加。
另请注意,在大多数测试中,我只是访问元素。当您开始对它们应用适当的函数时,执行时间的增量将会增加。
我也明白这不是测试性能的最科学的方法,但是,为了大致了解这些变化对性能的影响程度,我认为这已经足够了。
最後,在大多數網路應用程式中,相關網路伺服器的連線速度和回應時間對應用程式效能的影響比對程式碼的調整影響更大。儘管如此,這仍然是重要的信息,當您試圖從程式碼中獲得盡可能多的性能時,它將幫助您。
我們就完成了。當您嘗試優化程式碼時需要記住以下幾點;當然,這並不是所有的調整列表,而這些要點不一定適用於所有情況。無論哪種方式,我都會密切關注評論,以閱讀您對這個主題的看法。你看到這裡有什麼錯誤嗎?請在下面留言給我。
有疑問嗎?好話要說嗎?批評?點擊評論部分並給我留言。快樂編碼!
以上是為初學者測試和增強 jQuery 程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!