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中文网其他相关文章!