内容


高级 jQuery

让好的应用程序变成强大的应用程序

Comments

简介

这个关于 jQuery 的系列文章帮助您提高了创建基于 JavaScript 的 Web 应用程序的能力。即使您在阅读这些文章之前从未听说过 jQuery,但是您应该已经具备了使用 jQuery 构建良好的 Web 应用程序所需的语言技能和背景知识。但是,有时候良好的应用程序还不能满足需求,这时您将需要一个强大的 Web 应用程序。这就需要通过几个步骤修改现有的应用程序,让它能够在各种场合中顺利运行,并且适合所有用户。这些步骤是优化您的 Web 应用程序的最后绝招。

在本文中,我将讨论代码的性能改进,同时也谈及一些关于 jQuery 库的容易被忽视的问题。有些东西对于复杂的应用程序而言至关重要,即对所有应用程序都非常关键的插件,以及使应用程序的编写更加容易的良好设计技巧。在最后一个小节中,我将讨论 jQuery 1.3 中的一些新特性,它们是最近发布的并且为 jQuery 库添加了一些新特性。

第一个示例应用程序

本文中的大部分技巧都可以从附带的样例应用程序中找到(见 下载),这是一个直观的电子邮件 Web 应用程序。您可能对它比较熟悉,因为我在本系列的第一篇文章中使用的就是它。不过,您可以看到它是如何从第一篇文章中发展而来的,它的性能是如何改进的,以及这些最后步骤如何将它转变成强大的 Web 应用程序的。

图 1. 样例应用程序
样例应用程序
样例应用程序

Bind/Unbind

在 Events 模块中有两个函数,它们是 bind()unbind(),用于完成所有其他事件方法的任务。如果您能够向页面元素添加一个 click() 方法,那么哪还有必要调用 bind("click") 呢?这仅是浪费时间而已。但是,这些函数在特定情况下是非常方便的,如果正确地使用它们,可以显著提高应用程序的性能。这些函数不仅能够向特定页面元素添加事件(就像该模块中的许多其他事件方法一样),而且还可以从页面元素中删除这些事件。为什么要这样做?下面我们看看这个 Web 应用程序,以及如何在特定情况下使用这些函数。

图 2. Bind/Unbind 示例
这个图表显示了带有各种元素的注释的收件箱片段。Select All/Deselect All 复选框位于左上角。单独的复选框随每个消息行一起运行。消息 “You have 5 selected messages” 在每次选择复选框时都更新,对勾选复选框的行数进行求和。
这个图表显示了带有各种元素的注释的收件箱片段。Select All/Deselect All 复选框位于左上角。单独的复选框随每个消息行一起运行。消息 “You have 5 selected messages” 在每次选择复选框时都更新,对勾选复选框的行数进行求和。

清单 1 显示了以上设置的代码,这是未改进之前的原始代码:

清单 1. 未经过优化的小部件
$(document).ready(function(){
   // cache this query since it's a search by CLASS
   selectable = $(":checked.selectable");
   // when the select/deselect all is clicked, do this function
   $("#selectall").click(selectAll);
   // whenever any individual checkbox is checked, change the text
   // describing how many are checked
   selectable.click(changeNumFilters);
   // calculate how many are initially checked
   changeNumFilters();
});

var selectable;

function changeNumFilters()
{
   // this needs to be checked on every call
   // since the length can change with every click
   var size = $(":checked.selectable").length;
   if (size > 0)
      $("#selectedCount").html(size);
   else
      $("#selectedCount").html("0");
 }
 
// handles the select/deselect of all checkboxes
function selectAll()
{
   var checked = $("#selectall").attr("checked");
   selectable.each(function(){
      var subChecked = $(this).attr("checked");
      if (subChecked != checked)
      {
         $(this).click();
      }
   });
   changeNumFilters();
   }

该代码看起来比较简单,因为我在好几篇文章中都用到这个小部件。您在第一篇文章中见到了 “select/deselect all” 小部件,我给出了它的基础形式。在关于性能的文章中,您看到如何通过缓存选择查询和通过 CLASS 减少使用查询来改进它的性能。但是还有一个问题需要解决。当在包含 100 行的表中勾选 “select/deselect all” 复选框之后,您将得到糟糕的性能。事实上,在我的浏览器中,如果使用了这些代码,那么完成选择的平均时间为 3.4 秒。响应性太差了!即使进行了各项优化,仍然有些不可接受的地方。

让我们深入一步考察这个算法,看看是否有地方出了问题。您将遍历页面上的每个复选框,看看它们当前的 “checked” 状态是否与 “select/deselect all” 复选框一致。如果不一致,就对其调用 “click”,以和 “select/deselect all” 复选框的状态匹配。等一等,您还需要向这些复选框添加一个函数,从而在每次单击时都调用 changeNumFilters() 函数。通过仔细检查,您发现设置了一个可能调用 changeNumFilters() 101 次的算法。怪不得性能如此差。很明显,您不需要在每次单击时都更新选中的消息的计数,而是在该过程完成之后进行更新即可。在单击复选框的同时如何才能避免调用该方法?

现在,unbind() 方法开始发挥它的作用。通过在单击复选框之前调用 unbind(),将停止调用 click(),同时避免了 click() 进一步调用 changeNumFilter() 方法。这很棒!现在就不会调用 changeNumFilters() 101 次了。但是,这仅是一次有效的,在调用 click 方法之后,需要使用 bind 方法将 click 方法添加回到每个复选框。清单 2 显示了更新之后的小部件。

清单 2. 经过优化的小部件
// handles the selection/unselection of all checkboxes
function selectAll()
{
   var checked = $("#selectall").attr("checked");
   selectable.unbind("click", changeNumFilters);
   selectable.each(function(){
      var subChecked = $(this).attr("checked");
      if (subChecked != checked)
      {
          $(this).click();
      }
   });
   selectable.bind("click", changeNumFilters);
   changeNumFilters();
   }

通过这些优化之后,复选框的运行速度提高到约 900 毫秒,从而大大改进了性能。这些改进源于返回去检查您的算法正在做什么,以及贯穿代码的操作。您可以仅调用函数 1 次,而不是 100 次。通过在本系列的其他文章中不断改进该函数,您最后会让它变得更快、更高效。但不一定非得这么做,我还发现一个最快的算法,以前从来没有透露过。此外,如果我过早地向您展示这个最快的算法,我就不能将其作为本文的题材了。希望它能使您看到在代码中使用 bind/unbind 特性带来的好处(如果没有更好的方法的话)。

记住:在不希望触发默认事件时才使用 bind/unbind,或作为向页面元素添加或删除事件的临时方法

清单 3 显示了编写该算法的最快方法(如果您的代码中有这个小部件)。它运行该函数仅需 40 毫秒,远远胜过之前的其他方法。

清单 3. 使用超快算法的小部件
function selectAll()
{
   var checked = $("#selectall").attr("checked");
   selectable.each(function(){
      $(this).attr("checked", checked);
   });
   changeNumFilters();
   }

Live/Die

jQuery 1.3 版本的另外两个强大的新特性是 live()die() 函数。通过一个示例可以看到它对构建设计良好的 Web 应用程序的作用。想像一下对表中的每个单元格都添加一个双击。作为 jQuery 老手,您应该知道要在 document.ready() 函数中设置双击,如清单 4 所示。

清单 4. 设置双击
$("tr.messageRow").dblclick(function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
   }
   });

这个设计存在一个问题。它向包含一个 messageRow 类的表的每行添加一个双击事件。但是,如果向该表添加新的行,会发生什么事情呢?例如,当您使用 Ajax 在未重新加载页面的情况下将额外的消息加载到页面时,可能会显示这些行。这导致一个问题,因为所编写的代码不能工作。您创建的事件被绑定到所有在加载页面时显示的 tr.messageRow 元素中。它没有绑定到您在页面的生命周期中创建的任何新的 tr.messageRow 中。编写类似代码的程序员最终会很失望的,因为它无法工作。在 jQuery 文档发现该问题之前,jQuery 新手可能要花好几个小时才能弄明白为什么他们的代码不能工作(这也是我去年的经历)。

在 jQuery 1.3 之前,有 3 种可以解决该问题的方法,但都不是很好(对于使用 jQuery 1.2.x 的程序员而言,它们仍然有效)。第一种方法是重新初始化技术,它在每次添加新的元素之后重新将事件添加到选中的元素中。第二种方法利用了 bind/unbind,如前面小节所示。清单 5 显示了这两种方法。

清单 5. 向新元素添加事件的替代办法
// first technique to deal with "hot" page elements, added during the page's
// lifetime
$("#mailtable tr #"+message.id).addClass("messageRow")
   .dblclick(function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
   }

// second technique to deal with "hot" page elements
$("#mailtable tr #"+message.id).addClass("messageRow")
   .bind("dblclick", (function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
      }

这两种方法都不是很好。您可能正在重复编写代码,或者被迫查找可能添加新页面元素的点,然后在这些点上处理 “热元素” 问题。这不是良好的编程方式。但是,jQuery 可能大大简化这一切,它能够帮助我们完成很多事情。

幸运的是,有一个插件好像能够解决该问题。这个插件称为 LiveQuery 插件,它允许您将特定页面元素绑定到事件,但仅能以 “活动” 的方式进行,因此所有页面元素(包括创建页面时自带的元素和在页面的生命周期中创建的元素)都可能触发事件。对 UI 开发人员而言,这是一个非常智能、重要的插件,因为使得处理动态页面就像处理静态页面一样简单。对于 Web 应用程序开发人员而言,它就是真正不可或缺的插件。

jQuery 核心团队意识到该插件的重要性,从而将其包含到 1.3 发布版中。这个 “活动” 特性现在是核心 jQuery 的一部分,因此任何开发人员都可以利用它。这个特性完整地包含在 1.3 核心代码中,除了一小部分事件之外。我敢打赌,这些未被包含的事件将出现在 jQuery 的下一个发布版之中。我们看看如何利用它改变代码。

清单 6. “活动” 事件模型
$("tr.messageRow").live("dblclick", function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
      }

通过对代码进行一处小更改,该页面上的所有 tr.messageRow 元素被双击时都将触发这段代码。仅使用 dblclick() 函数是看不到这种行为的,如上所述。为此,我极力推荐您在大部分事件方法中使用 live() 方法。事实上,我认为它对于任何动态地创建页面元素的页面都是必不可少的,不管是通过 Ajax 还是用户交互进行创建,都需要使用 live() 函数而不是事件方法。它很好的实现了易编写和 bug 之间的折衷。

记住:当将事件添加到动态页面元素时要使用 live() 方法。这让事件和页面元素一样具有动态性。

Ajax Queue/Sync

在服务器中使用 Ajax 调用成为 Web 2.0 公司度量自身的度量指标。我们已经多次讨论过,在 jQuery 中使用 Ajax 就像调用普通的方法一样简单。这意味着您可以轻松地调用任何服务器端 Ajax 函数,就像调用客户端的 JavaScript 函数一样。但是美中存在一些不足之处,当对服务器进行过多的 Ajax 调用时,就会出现一些负面效应。如果 Web 应用程序使用的 Ajax 调用过多,就会导致问题。

第一个问题是一些浏览器限制打开的服务器连接的数量。在 Internet Explorer 中,当前版本仅支持打开 2 个服务器连接。Firefox 支持打开 8 个连接,但仍然是一个限制。如果 Web 应用程序不对 Ajax 调用进行控制,它就很可能打开 2 个以上的连接,尤其是服务器端调用属于时间密集型调用时。这个问题可能源于 Web 应用程序的不良设计,或用户不对请求加以限制。不管是那种情况都是不可取的,因为您不希望由浏览器决定使用哪些连接。

另外,因为调用是异步的,不能保证从服务器返回的响应的顺序与发送时一样。例如,如果您几乎同时发出 2 个 Ajax 调用,您就不能保证服务器的响应是以相同的顺序返回。因此,如果第二个调用依赖于第一个调用的结果,那么就会出现问题。想象这样一种场景,其中第一个调用获取数据,第二个调用在客户端操作该数据。如果第二个调用的响应返回得比第一个 Ajax 调用快,那么您的代码就会导致错误。您完全不能保证响应速度。当调用更多时,就更容易导致问题。

jQuery 的创建者意识到这个潜在的问题,但同时也认识到它仅会给 1% 的 Web 应用程序带来问题。但 1% 开发 Web 应用程序的开发人员需要一个解决办法。因此创建了一个插件,通过创建一个 Ajax Queue 和一个 Ajax Sync 来筛查该问题。Queue 和 Sync 的功能是很相似的:Queue 一次发出一个 Ajax 调用,并且等待其响应返回之后才发出另一个调用。Sync 几乎同时发出多个调用,但调用的响应是按先后顺序返回的。

通过在客户端控制 Ajax 调用解决了超载问题,同时也控制和规范了将响应发送回客户端的方式。现在,您可以确保知道响应返回到客户端的顺序,从而可以根据事件的顺序编写代码。我们看看这个插件是如何工作的,以及如何在您的代码中使用它(见清单 7)。记住,这仅是为 1% 的 Web 应用程序设计的,它们拥有多个 Ajax 调用,并且后一个调用严重依赖于前一个调用的结果。这个示例不是调用相互依赖的例子,但它能够向您展示如何使用该插件(要为这个插件的应用创建一个完美的真实例子,并让其易于理解是很困难的)。

清单 7. Ajax Queue
var newRow = "<tr id='?'>" +
             "<td><input type=checkbox value='?'></td>" +
             "<td>?</td>" +
             "<td>?</td>" +
             "<td>?</td>" +
             "<td>?</td></tr>";
   
   
$("#mailtable").everyTime(30000, "checkForMail", function(){

   // by using the Ajax Queue here, we can be sure that we will check for mail
   // every 30 seconds, but ONLY if the previous mail check has already returned.
   // This actually would be beneficial in a mail application, if one check for
   // new mail takes longer than 30 seconds to respond, we wouldn't want the
   // next Ajax call to kick off, because it might duplicate messages (depending
   // on the server side code).
   // So, by using the Ajax Queue plug-in, we can ensure that our Web client
   // is only checking for new mail once, and will never overlap itself.

    $.ajaxQueue({
         url: "check_for_mail.jsp",
         success: function(data)
         {
           var message = eval('(' + data + ')');
           if (message.id != 0)
           {
             var row = newRow.replace("?", message.id);
             row = row.replace("?", message.id);
             row = row.replace("?", message.to);
             row = row.replace("?", message.from);
             row = row.replace("?", message.subject);
             row = row.replace("?", message.sentTime);
             $("#mailtable tbody").prepend(row);
             $("#mailtable #"+message.id).addClass("mail_unread").addClass("messageRow");
             $("#mailtable #"+message.id+ " td").addClass("mail");
             $("#mailtable :checkbox").addClass("selectable");
            }
          }
          });

记住:如果您的应用程序有多个相互依赖的 Ajax 调用,那么要考虑使用 Ajax Queue 或 Ajax Sync。

第二个示例 Web 应用程序

我将使用另一个小部件解决本文的最后 3 个问题,并且在深入研究其代码之前展示和解释它。这个 401k 小部件并不陌生,因为您已经在前面的文章见过它(参见 参考资料 部分获取这些文章的链接)。不过,这回有个微妙的不同之处,因为我在同一个页面上两次添加了这个小部件。它被添加到两个不同的表中。这将带来几个有趣的地方。图 3 显示了这个小部件:

图 3. 401k 小部件
这个屏幕截图显示了 401k 小部件的两个版本,并且带有说明。
这个屏幕截图显示了 401k 小部件的两个版本,并且带有说明。

在这个小部件中,我正在做几件事情。第一件是计算文本字段之和并确定它们是否为 100。如果它们的和不为 100,我将向用户显示一个错误,提示他们没有正确使用该小部件。第二,我在每个选项获取输入之后对选项进行排序。通过这种方式,百分比最高的投资分配将一直出现在表的顶部。这可以在图 3 中看到,它按百分比对选项进行排序。最后,为了让它更酷,我添加了一些条带。

用于生产这个小部件的 HTML 代码出奇地简单。清单 8 详细地显示了这个小部件。

清单 8. 生成 401k 小部件的 HTML
<p><table width=300 class="percentSort" cellpadding=0 cellspacing=0>
<tbody>
  <tr><td>S&P 500 Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>Russell 2000 Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>MSCI International Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>MSCI Emerging Market Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>REIT Index</td>
  <td><input type=text> %</td></tr>
</tbody>
<tfoot>
</tfoot>
</table>

用 jQuery 设置小部件

以上的一小段 HTML 直接引入了这部分内容,本小节关注如何在 jQuery 中设置小部件,以及所需的所有代码。要将事件附加到页面元素或在特定情况下需要添加类时,通常需要这样做。有时候还需要更进一步。这些小部件的所有设置代码都是 jQuery 代码。我可以提供关于角色分离的理论,让 HTML 设计师和 JavaScript 程序员各自完成自己的工作,但是您们可能已经多次听到这种陈词。在这里我仅添加另一样东西,即 “类修饰”,这是很多插件创作者都使用的。看看清单 8 中的 HTML 代码,仅通过将一个 percentSort 类添加到表,您就可以显著改变表的功能和外观。这是小部件设计的目标,让添加和删除小部件就像向小部件添加类一样简单。

让我们遵循我曾用 jQuery 设置小部件的几个步骤。通过查看这些步骤,您可以看到清单 9 中的设计模式是如何出现的。

清单 9. jQuery 小部件代码
$(document).ready(function() {

  // the first step is to find all the tables on the page with
  // a class of percentSort.  These are all the tables we want to
  // convert into our widget.
  // After we find them, we need to loop through them and take some
  // actions on them
  // At the conclusion of this block of code, each table that's going to
  // be a percentSort widget will have been transformed

  $("table.percentSort").each(function(i){

     // each table needs a unique ID, for namespace issues (discussed later)
     // we can simply create a uniqueID from the loop counter

     $(this).attr("id", "percentSort-"+i);

     // within each table, let's highlight every other row in the table, to
     // give it that "zebra" look

     $(this).find("tbody > tr").filter(":odd").addClass("highlight");

     // because each table needs to show the "Total" to the user, let's create a new
     // section of the table in the footer.  We'll add a row in the table footer
     // to display the words "Total" and a span for the updated count.

     $("#"+$(this).attr("id") + " tfoot")
          .append("<tr><td>Total</td><td>
          <span></span> %</td></tr>");

     // finally, let's add the CLASS of "percentTotal" to the span we just
     // created above.  We'll use this information later to display
     // the updated totals

     $("#"+$(this).attr("id") + " tfoot span").addClass("percentTotal");
  });

  // now the second step, after we've completed setting up the tables themselves
  // is to set up the individual table rows.
  // We can similarly sort through each of them, taking the appropriate actions
  // on each of them in turn.
  // Upon completion of this block of code, each row in each table will be
  // transformed for our widget

  $("table.percentSort tbody > tr").each(function(i){

     // get the namespace (to be discussed in the next section)

     var NAMESPACE = $(this).parents("table.percentSort").attr("id");

     // attach a unique ID to this row.  We can use the loop counter
     // to ensure the ID is unique on the page (which is a must on every page)

     $(this).attr("id", "row"+i);

     // now, within this row of the table, we need to find the text input, because
     // we need to attach a class to them.  We utilize the namespace, and also
     // find the :text within the table row, and then attach the correct class

     $("#"+$(this).attr("id") + " :text").addClass("percent");

     // Finally, we attach a unique ID to each of the text inputs, and we do this by
     // making it a combination of the table name and the row name.

     $("#"+$(this).attr("id") + " .percent").
               attr("id", NAMESPACE + "-" + $(this).attr("id"));
  });


  // Finally, because we know we only want numerical inputs, we restrict the text entry
  // to just numbers.  We must do this now, because up until this point, the page
  // contained no elements with the CLASS "percent"
  $(".percent").numeric();

如您从这个例子中见到的一样,可以通过 jQuery 代码向 HTML 代码引入大量功能。这种类型的设计的好处是很明显的。同样,遵循角色分离、代码重用等也是非常有益的。您还将在小部件插件中看到这种类型的设计,因为它将简单的 HTML 代码转变成适用于插件的小部件。最重要的是,这也是您在这里需要完成的任务,即编写一个插件来将一个简单的表转变成排序和汇总表。

记住:尽量多使用 jQuery 代码进行设置,并且尽可能少使用 HTML。

名称空间

可能处理这种类型的设计和 jQuery 的最难解方面是理解名称空间。这个例子很好,因为它以一种很直观的方式展示了该问题。知道页面上的所有 ID 和类时,编写 jQuery 代码是非常简单的。这就是 jQuery 的设计目标。当您不知道页面上有几个类时,或这些类开始重复时,会发生什么呢?在这个例子中,您可以直接看到问题,有两个完全相同的小部件!所有东西都是重复的。这似乎是不考虑名称空间引起的问题;您的小部件开始彼此干扰,最终导致不能正常工作。

图 4. 忽略名称空间带来的问题
显示大小为 401k 的小部件的两个版本,并且带有说明 “Enter the percentage allocation you wish to place YOUR OWN 401k contributions into”。下面是许多生成投资选项的行,以及后面带百分号的文本输入框。
显示大小为 401k 的小部件的两个版本,并且带有说明 “Enter the percentage allocation you wish to place YOUR OWN 401k contributions into”。下面是许多生成投资选项的行,以及后面带百分号的文本输入框。

造成这个问题的原因是您仅调用 $(".percentTotal") 等,而忽略了应该同步哪个小部件。因为相同的页面上有多个表,因此该页面就有 percentTotal 类的多个实例。当然,如果页面上仅有一个表,那么您就可以确定它是唯一的。但是随着页面更加高级,以及组件的重用越来越多,这种一个表的假设就不再成立。有些人会问,“在这里使用 ID 不行吗?” 这不能解决问题:您打算给它什么 ID?您不可以使用 “percentTotal”,因为这会带来歧义。也不可以使用 “percentTotal-1”,因为它不表示页面上的任何东西。(以上数字毕竟是任意创建的)。您可以向页面包含的表添加一些引用来解决问题,比如 “percentTotal-percentSort1”,但这会让问题更加复杂。jQuery 有一个超级复杂但又十分容易使用的选择语法,从而让这种混合命名模式变得毫无必要。为什么要重新创造已有的东西呢?让我们使用 jQuery 的选择引擎帮助您解决名称空间问题。

这个小部件的核心问题是决定操作发生在哪个小部件中。当向文本框输入数字时,您可以问自己,jQuery 如何知道进入哪个文本框?您在代码中的 “百分比” 类添加了一个事件,并且可以在代码内部使用 $(this) 引用它。这将我们带入下一个问题:jQuery 如何知道问题发生在哪个小部件中,从而使您能够更新正确的 percentTotal 字段?我认为这不是一件简单的事情。这确实不简单。尽管您的代码能够向页面上带有 “百分比” 类的每个文本框添加一个事件,但如果忽略了发生事件的小部件就是不妥当的。

这个问题被归结为名称空间问题的原因是,小部件的命名不清晰,容易导致问题。要使 jQuery 代码正常工作,每个名称都必须在其自己的空间之内明确定义,这就出现了术语名称空间。您必须避免编写重名的代码,实现每个小部件都是自含的。您必须能够在相同的页面上添加相同小部件的多个实例,同时避免名称重复。最重要的是,每个小部件在页面上都是独立的。

没有能够处理名称空间问题的现成方法,因此我将展示我的解决办法,您可以在自己的代码中使用我的办法,或通过了解问题创建更好的解决办法。我喜欢该代码的原因是它简单易用(只有 1 行),并且能够让您在某种程度上控制自己的名称空间。看看清单 10。

清单 10. 名称空间解决办法
// our event is attached to EVERY input text field with a CLASS of "percent"
// this makes our code look good, but can lead to namespace issues
$("table.percentSort input.percent").keyup(function(){

   // this simple line can establish a namespace for us, by getting the unique
   // ID attached to our table (the table is considered the "container" for
   // our widget.  It could be anything, depending on your specific code.)
   // We pass the CLASS of our widget to the parents() function, because
   // that effectively encapsulates the widget

   var NAMESPACE = "#" + $(this).parents("table.percentSort").attr("id");

   // with the namespace established, we can use the jQuery selection
   // syntax to use this namespace as our prefix for all of our remaining
   // searches.  Notice how the ambiguity is removed for our search
   // by CLASS of "percent" and "percentTotal"
   // This solves our namespace issues

   var sum = $(NAMESPACE + " input.percent").sum();
   var totalField = $(NAMESPACE + " .percentTotal");

因此,仅需添加一行代码,您就能够封装小部件,从而避免它的函数与自身的其他实例重复(或者甚至是其他碰巧对 ID 或类使用相同名称的小部件)。这种类型的代码编写方式在插件代码中很常见。编写良好的插件应该考虑到名称空间问题,而糟糕的插件忽略了这个问题。从这个例子中可以看到,在代码中使用名称空间也是很简单的,并且随着页面越来越复杂,它可以给您节省大量时间。鉴于这个原因,我建议您现在就开始考虑 jQuery 代码中的名称空间问题,并在编写代码时使用这种解决办法。

记住:开始编写 jQuery 代码时,一般就要开始包含名称空间解决办法。您可以使用以上的解决办法,或创建自己的解决办法。通过采用名称空间解决办法,即使代码越来越复杂,也不会带来名称重复问题。

jQuery 1.3 中的新特性

在本文的最后小节,我打算过一遍 jQuery 1.3 中包含的新特性。jQuery 1.3 发布版在性能上是很出色的,光凭这点,您就应该将代码迁移到这个版本。不过,有几个新添加的小特性可以帮助您改善代码。

添加到 jQuery 1.3 核心发布版的第一个新特性是 live()/die() 函数,我已经在本文的前面讨论过它们。这可能是最重要的一个新特性,因为它们值得花一个小节去讨论。添加到 1.3 核心发布版的另一个主要特性是 jQuery.Event 类,它将页面上发生的事件封装在一个 Object 中。这对事件密集型应用程序尤其有益,因为它提供一个良好自含的对象,用于传播关于事件的所有信息。这些信息包括类型、目标、X 和 Y 坐标,甚至是时间戳。在 1.3 发布版之前可能也提供这些信息,但它的归档没有这么好,并且封装也没有这么好。最后添加到 1.3 发布版的新特性对所有开发人员都是透明的,但仍然值得一提。在这个发布版中,不再需要仔细区分浏览器,因为有一个针对浏览器或版本的特殊 if 语句。您可以想象一下处理所有支持的浏览器的代码大拼盘。这在某种程度上会促使 jQuery 版本衰退,因为浏览器的版本更新太快。相反,它们检查浏览器中的功能,但又不考虑浏览器的类型/版本。这意味着即使推出了新的浏览器,老浏览器退役,仍然不需要更新 jQuery 的版本。对于不希望每年更新 jQuery 版本并进行相关测试的站点而言,这是个好消息。

结束语

本文提供一些技巧,帮助您将良好的 jQuery 代码转变成强大的 jQuery 代码。jQuery 非常简单易用(并且是独立的 JavaScript 的巨大改进),因此编写良好的 jQuery 代码也很容易。大部分开发人员在几分钟之内编写完并运行良好的 jQuery 代码。但是良好的代码和强大的代码是有区别的。强大的 jQuery 代码考虑随着页面越来越复杂时的性能问题。强大的 jQuery 代码能够考虑到页面的未来方向,而不是仅看到当前的位置。强大的 jQuery 代码是为最复杂的应用程序设计的,然后让应用程序处理输入的简单信息。

本文介绍了 5 个概念,帮助您将良好的 jQuery 代码转变成强大的 jQuery 代码。第一个概念是使用 bind()/unbind() 方法。当您不希望在页面的生命周期内将事件添加到代码时,这些方法对向页面元素添加/移除事件非常有用。这些方法在页面包含大量事件时对提升性能非常重要,或者用于某些用户界面中。第二个概念是使用 1.3 中包含的新特性 live()/die()。这些函数允许将事件变成动态的,就像页面元素一样。随着 Web 应用程序包含的页面元素越来越多,这些函数允许代码随着页面的增长而增长,这在以前的发布版中是无法实现的。您希望事件处理像页面处理一样具有动态性。第三个新添加的特性是 Ajax Queue/Sync 插件,它用于规范和控制对服务器发出的 Ajax 调用,避免它们超出限度(从客户端角度看)。当 Ajax 调用的响应返回顺序很重要时,该插件也能帮上大忙。第四个概念是,尽可能多地用 jQuery 代码编写页面设置代码。这让 HTML 的编写更加简单,并且在设置页面时能够获得更多的控制。最后一个概念是,在代码中利用名称空间解决办法,避免因小部件的函数名称重复而导致问题。每个页面元素和小部件都应该是自含的,不与页面的其他方面发生干扰,名称空间解决办法能够阻止该问题。

这 5 个步骤并不难实现。事实上,其中 4 个步骤仅需修改一行代码。不过,理解如何在代码中应用它们才是最重要的。像所有东西一样,如果不能正确时使用,它不仅不能提供帮助,反而还有害处。我的建议是,当您用 jQuery 代码编写页面时,要尽快应用这 5 个步骤。每个开发人员都会告诉您,利用特性是编程过程的一部分。您不希望仅因开头设计得不好或更改某些部分而重新设计整个 Web 应用程序。编写代码时就要抱有编写强大应用程序的想法,并且遵循这些建议。

最后,这是我的 jQuery 系列文章的第二个周期的末尾。这最后 5 篇文章将您的 jQuery 水平提升一个新的级别,现在您应该能够使用 jQuery 库创建任何类型的应用程序。我的最后一点建议是经常以实验的方式编写代码。您不仅能够学到很多东西,还将创建出更酷的 Web 应用程序!


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=424237
ArticleTitle=高级 jQuery
publish-date=08312009