jQuery性能指标和调优(1)(2)
jQuery 性能调优
本文的第二部分将讨论如何改进 jQuery 代码的性能。前一部分表明选择 jQuery 作为 JavaScript 库指向了正确的性能方向。如果您正在阅读本文,您可能已经使用了 jQuery。但是底层库速度快并不意味着您编写的所有代码都是高质量的。如果您没有回过头来想想应该怎么做,使用 jQuery 仍然会编写出非常慢的代码。
这个部分介绍一些性能调优知识,以及改进 jQuery 代码速度的最佳实践技巧。
技巧 #1 - 尽可能多地通过 ID 进行搜索,而不是 CLASS
在 jQuery 代码中两种常见的搜索技术是通过元素的 ID 进行搜索和通过元素的 CLASS 进行搜索。在使用常规 JavaScript 的 JavaScript 库之前,通过 ID 查找页面元素还是相当简单的。可以使用 getElementById() 方法快速找到元素。但是如果没有 JavaScript 库,要查找 CLASS 会更加困难,在必要情况下,我们还通过在其 ID 中进行编码帮助查找。使用 jQuery 时,搜索 CLASS 就像搜索页面上的 ID 一样简单,因此这两个搜索似乎是可互换的。然而实际情况并非如此。通过 ID 搜索比通过 CLASS 搜索要快得多。当通过 ID 进行搜索时,jQuery 实际上仅使用内置的 getElementById() 方法,但通过 CLASS 进行搜索时必须遍历页面上的所有元素,以查找匹配项。很明显,当页面越大并且越复杂时,通过 CLASS 进行搜索会导致响应非常慢,而通过 ID 进行搜索不会随着页面变大而变慢。
前面运行的 jQuery 性能测试结果支持这一数据。让我们查看每个测试,看看需要注意 jQuery 代码的什么地方。在这个例子中,分别看看通过 ID 和 CLASS 进行搜索时的测试结果(图 5)。
图 5. ID 搜索和 CLASS 搜索对比
这些测试是不同的,但它们得出的数据表明通过 ID 进行搜索比通过 CLASS 进行搜索快得多。这如何影响到 jQuery 代码?在编写搜索时,您要记住这些技巧:如果既可选择 CLASS 又可选择 ID,那么通常要选择 ID。如果需要在您的代码中搜索某些元素,一定要给它们分配 ID。
清单 1 显示了一个实际的 jQuery 测试,您可以在您的机器上运行它对此进行验证:
清单 1. CLASS 和 ID
- $(document).ready(function() {
- console.info("Start Test");
- var d = new Date();
- console.info(d.getSeconds() + " " + d.getMilliseconds());
- var testBody = "";
- for (var i=0; i<1000; i++)
- {
- testBody += "<div class='testable"+i+"'>";
- }
- $("body").append(testBody);
- for (var i=0; i<1000; i++)
- {
- $(".testable"+i);
- }
- var d = new Date();
- console.info(d.getSeconds() + " " + d.getMilliseconds());
- console.time("Start ID Test");
- testBody = "";
- for (var i=0; i<1000; i++)
- {
- testBody += "<div id='testable"+i+"'>";
- }
- $("body").append(testBody);
- for (var i=0; i<1000; i++)
- {
- $("#testable"+i);
- }
- var d = new Date();
- console.info(d.getSeconds() + " " + d.getMilliseconds());
- console.info("End Test");
- });
ID 测试耗时 218 毫秒,而 CLASS 测试耗时 19.9 秒!甚至在专门为该测试构建的简单页面上,ID 搜索也要比 CLASS 搜索快 100 倍!
技巧 #2 - 提供尽可能多的搜索信息
jQuery 提供如此多的页面元素搜索方法,有时您难以指出哪种方法是最好的。有一条经验是不会错的,即为搜索参数提供尽可能多的信息。因此,假如您正在搜索带有 “clickable” CLASS 的所有页面元素,如果您提前知道仅有 DIV 附带有 CLASS,那么就能提高搜索性能。所以,搜索 “div.clickable” 会更加快。图 6 显示了支持该技巧的结果。
图 6. 尽可能多地提供信息
考虑到底层 JavaScript 代码之后,这就不足为奇了。通过提供元素标记,与 CLASS 参数匹配的搜索元素数量将大大减少,从而将搜索性能提升至与本例中的 ID 搜索相当。
开发人员在编写 jQuery 选择方法时不能偷懒,尽管 jQuery 的简单让人产生偷懒的欲望。简单让您放松了警惕。搜索机制变得如此简单,让我们倾向于仅输入一条信息。然而,您应该总是尽可能多地输入信息,尤其是已知信息。清单 2 显示了一个很好的例子。
清单 2. 提供充足的信息
- // Assume there are 50 of these in some giant form, and you need to validate
- // these fields before they are submitted, and there are hundreds of other
- // elements on the page as well
- <input type=text class="notBlank">
- // the "bad" way to validate these fields
- $(".notBlank").each(function(){
- if ($(this).val()=="")
- $(this).addClass("error");
- });
- // the "good" way to validate these fields
- $("input.notBlank").each(function(){
- if ($(this).val()=="")
- $(this).addClass("error");
- });
- // the "best" way to validate these fields
- $("input:text.notBlank").each(function(){
- if ($(this).val()=="")
- $(this).addClass("error");
- });
技巧 #3 - 缓存选择器
最后一个性能技巧利用了几乎所有 jQuery 选择器都返回 jQuery 对象这个特性。这意味着在理想的情况下,您仅需要运行选择器一次,并且能够轻松地将所有函数连接在一起,或缓存结果供以后使用。您也不要担心缓存,因为与总体可用内存相比,返回的对象是很小的。
清单 3 给出了一些关于如何利用缓存的例子。
清单 3. 缓存
在我的最后一个关于性能
- // Imagine a function that hides all the div's with a class of "hideable"
- // when a button is pressed. These DIV's might reappear later when
- // working with the page, so the button can be pressed any number of
- // times, and the DIV's that have reappeared
- // will again be made to be hidden.
- $("#ourHideButton").click(function(){
- $(".hideable").hide();
- });
- // As you saw in the CLASS versus ID example, though, a search for
- // CLASS is very inefficient
- // If this button is pressed often, it could lead to a slow response
- // Instead of the above example, you should write your code like this
- var hideable;
- $("#ourHideButton").click(function(){
- if (hideable)
- hideable.hide();
- else
- hideable = $(".hideable").hide();
- });
- // You can cache your search in a JavaScript variable and reuse it every time
- // the button is pressed. Because jQuery almost always returns the
- // jQuery object, you can save it the first time it is called for future use
的示例代码中,将查看我在本系列的第一篇文章中提到的小部件(见 参考资料)。这个小部件是在表的左上角上的复选框,它允许您选择或取消选择该列上的所有复选框。这个小部件在电子邮件应用程序中非常常见,用于选择或取消选择所有消息。
清单 4. 性能改进
- // Here is the code as I originally presented it in that article. Let's see
- // if we can improve the performance in any way from the things we learned here
- function selectAll()
- {
- var checked = $("#selectall").attr("checked");
- $(".selectable").each(function(){
- var subChecked = $(this).attr("checked");
- if (subChecked != checked)
- $(this).click();
- });
- }
- // Here's the improved function. The search for the ID of "selectall" is
- // OK as-is, because we saw how fast the ID search is.
- // The search for the CLASS of "selectable" was not well-designed though,
- // because we saw a search by CLASS is very inefficient.
- // First step was improving the search by supplying as much information as we know.
- // We narrowed the search to only checkboxes with the CLASS of selectable.
- // This should improve our search
- // Further, we can cache this search because we will only need to perform it once
- // Finally, we can perform this search before the selectall checkbox is even
- // checked (when the page is finished loading), so that the search is completed
- // and cached before the user even uses it.
- // These 3 simple performance steps gave me a 200% increase in speed when tested
- // on a page with 200 rows of data.
- var selectable = $(":checkbox.selectable");
- function selectAll()
- {
- var checked = $("#selectall").attr("checked");
- selectable.each(function(){
- var subChecked = $(this).attr("checked");
- if (subChecked != checked)
- $(this).click();
- });
- }
关于性能的要点
使用 JavaScript 时,速度和性能绝对不是小问题。在现实中,创建 jQuery 的开发人员和处理浏览器内置 JavaScript 引擎的开发人员都非常关注性能问题。事实上,在最近 6 个月以来,浏览器开发的最重要问题就是 JavaScript 引擎的速度。浏览器开发者都致力于在下一年迅速提升 JavaScript 的执行性能,从而大大提高 jQuery 代码和 JavaScript 引擎的速度。来自这些 “速度之战” 的消息表明,提升 JavaScript 性能是大势所趋。领导 jQuery 项目的 John Resig 一直都在谈论他的最新 “Sizzle” 选择引擎。他从头编写了一个选择引擎,并声称初步结果表明它比 Firefox 要快 4 倍。这是巨大的速度提升!在我撰写本文的最后部分时,jQuery 1.3 已经发布,并且包含了 Sizzle 选择引擎。jQuery 声称,在所有浏览器上运行的总体结果表明选择引擎的 1.3 版本比 1.2.6 版本的快 49%。此外,1.3 发布版在 HTML 注入(向 DOM 添加新的元素)上改进了 6 倍,在函数定位上改进了 3 倍。在我完成本文时,很多人都更新到了最新的 jQuery 发布版,这是非常令人激动的!
影响 JavaScript 性能的另一个因素是浏览器,如前所述,它的影响是所选的库的 9 倍。Firefox 和 Chrome 在 “最快 JavaScript 引擎” 之战中各有胜负,而 Safari 的参与让竞争更加激烈。从我们上面的测试中,可以看到 Chrome 在 JavaScript 引擎性能方面远远超过 Firefox,但是 Firefox 3.1 将包含新的 Tracemonkey JavaScript 引擎,届时其速度将比当前的 JavaScript 引擎 3.0 快 20 至 40 倍(这是他们声称的,不是我的观点),真不可思议!
在未来一两年内,您将看到底层 JavaScript 引擎和 JavaScript 库的速度得到巨大改进,从而导致使用 JavaScript 的 Web 应用程序将变得更加复杂。
如果您正在决定是使用 JavaScript 库还是自己编写 JavaScript,那么需要考虑的另一件事情是处理和调试 JavaScript 库所需的全部工作。最近几年以来,有数百位用户一直在维护库中的每一个函数。您可能要忙到深夜,甚至筋疲力尽地编写自己的函数。您更相信谁呢?另外,即使您能编写出比 jQuery 库更快的代码,您是否想过使用 jQuery 库能够获得更多的优势?您是否为了辛苦地编写自己的代码而放弃使用非常便利的 jQuery 及其函数库?自己编写代码不仅需要大量时间,并且还会产生更多 bug,因此我还是建议使用现成的 jQuery 库。
我最后讨论的要点可能会让一些人沮丧,但是我们必须考虑编写这些 jQuery 库的程序员。他们当中的一些是最棒的,并且他们编写的超级优秀的代码(一般人不能编写这样出色的代码)都收入到这些库中。我承认自己远远不如编写 jQuery 库的程序员。因此,为何不利用他们的智慧?
结束语
本文从总体上讨论了 jQuery 和 JavaScript 库的性能。通过对选择方法进行大量的测试,您看到这些库之间的速度存在巨大的差距,并且 jQuery 是最快的库之一。不过,即使您选择了最快的 JavaScript 库,还是不能解决 Web 应用程序的性能问题,因为浏览器对性能的影响比库强 9 倍。如果您能够控制用户使用特定的 Web 浏览器,那么就让他们使用最快的浏览器。找到能够最快地运行您的 Web 应用程序的浏览器,并让用户通过使用它从中受益。理想情况下,让最慢的 JavaScript 浏览器消失意味着出现更快的 Web 应用程序。
我还提供了关于 jQuery 性能的 3 个技巧。尽管有好几个站点都提供了关于 jQuery 性能的技巧,但是这 3 个技巧是最有效的。3 个技巧肯定比 50 个技巧容易记住,但其他技巧也是很好的,我将在 参考资料 部分指出另外一些技巧。不过,如果您在编写代码时记住了这 3 个技巧,将会获得巨大的性能提升。应该永远记住的 3 个 jQuery 技巧是:通过 ID 搜索比通过 CLASS 搜索快 100 倍,尽可能多地提供搜索信息,以及尽量缓存选择。
最后,我们快速查看了 jQuery 和浏览器的 JavaScript 引擎即将推出的改进。在我撰写本文的结尾部分时,jQuery 1.3 已经发布了,它承诺在选择和代码的其他方面实现跳跃式性能改进。此外,Firefox 还承诺它的下一代 JavaScript 引擎会快 20 至 40 倍!这些迹象表明,在未来的一两年内,JavaScript 环境会在性能上取得重大突破。在不久的将来,复杂的 Web 应用程序会日益流行,因为快速运行这些程序的条件已经成熟。
在本文结束时,我们应该回过头来重新检验这句话 “jQuery 在简单的页面上无可挑剔,但在复杂的页面上性能极其低下。在解决性能问题之前,您必须对复杂页面使用常规的 JavaScript。”根据我的经验,jQuery 的 “性能问题” 一样出现在 “常规的 JavaScript” 中。事实上,真正影响性能的不是 jQuery 或 JavaScript,而是浏览器。因此我同意这样的观点:使用设计不良的 jQuery 代码的复杂页面运行在 IE6 上时会导致糟糕的性能。不过,我希望您已经了解我要阐述的思想:只要拥有良好的 jQuery 代码、运用 3 个最重要的技巧并选择最快的浏览器,那么即使运行最复杂的页面也不会出现明显的性能问题。
原文链接:http://www.ibm.com/developerworks/cn/web/wa-aj-advjquery/








