CONAN ZONE

你越挣扎我就越兴奋

BlogJava 首页 新随笔 联系 聚合 管理
  0 Posts :: 282 Stories :: 0 Comments :: 0 Trackbacks

简介

这个关于 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” 在每次选择复选框时都更新,对勾选复选框的行数进行求和。

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


清单 1. 未经过优化的小部件

 1                 
 2 $(document).ready(function(){
 3    // cache this query since it's a search by CLASS
 4    selectable = $(":checked.selectable");
 5    // when the select/deselect all is clicked, do this function
 6    $("#selectall").click(selectAll);
 7    // whenever any individual checkbox is checked, change the text
 8    // describing how many are checked
 9    selectable.click(changeNumFilters);
10    // calculate how many are initially checked
11    changeNumFilters();
12 });
13 
14 var selectable;
15 
16 function changeNumFilters()
17 {
18    // this needs to be checked on every call
19    // since the length can change with every click
20    var size = $(":checked.selectable").length;
21    if (size > 0)
22       $("#selectedCount").html(size);
23    else
24       $("#selectedCount").html("0");
25  }
26  
27 // handles the select/deselect of all checkboxes
28 function selectAll()
29 {
30    var checked = $("#selectall").attr("checked");
31    selectable.each(function(){
32       var subChecked = $(this).attr("checked");
33       if (subChecked != checked)
34       {
35          $(this).click();
36       }
37    });
38    changeNumFilters();
39    }

该代码看起来比较简单,因为我在好几篇文章中都用到这个小部件。您在第一篇文章中见到了 “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. 经过优化的小部件

 1 // handles the selection/unselection of all checkboxes
 2 function selectAll()
 3 {
 4    var checked = $("#selectall").attr("checked");
 5    selectable.unbind("click", changeNumFilters);
 6    selectable.each(function(){
 7       var subChecked = $(this).attr("checked");
 8       if (subChecked != checked)
 9       {
10           $(this).click();
11       }
12    });
13    selectable.bind("click", changeNumFilters);
14    changeNumFilters();
15    }

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

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

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


清单 3. 使用超快算法的小部件

1 function selectAll()
2 {
3    var checked = $("#selectall").attr("checked");
4    selectable.each(function(){
5       $(this).attr("checked", checked);
6    });
7    changeNumFilters();
8    }

Live/Die

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


清单 4. 设置双击

1 $("tr.messageRow").dblclick(function() {
2    if ($(this).hasClass("mail_unread"))
3    {
4       $(this).removeClass("mail_unread");
5    }
6    });

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

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

清单 5. 向新元素添加事件的替代办法

 1 // first technique to deal with "hot" page elements, added during the page's
 2 // lifetime
 3 $("#mailtable tr #"+message.id).addClass("messageRow")
 4    .dblclick(function() {
 5    if ($(this).hasClass("mail_unread"))
 6    {
 7       $(this).removeClass("mail_unread");
 8    }
 9 
10 // second technique to deal with "hot" page elements
11 $("#mailtable tr #"+message.id).addClass("messageRow")
12    .bind("dblclick", (function() {
13    if ($(this).hasClass("mail_unread"))
14    {
15       $(this).removeClass("mail_unread");
16       }

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

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

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


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


通过对代码进行一处小更改,该页面上的所有 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
 1 var newRow = "<tr id='?'>" +
 2              "<td><input type=checkbox value='?'></td>" +
 3              "<td>?</td>" +
 4              "<td>?</td>" +
 5              "<td>?</td>" +
 6              "<td>?</td></tr>";
 7    
 8    
 9 $("#mailtable").everyTime(30000"checkForMail"function(){
10 
11    // by using the Ajax Queue here, we can be sure that we will check for mail
12    // every 30 seconds, but ONLY if the previous mail check has already returned.
13    // This actually would be beneficial in a mail application, if one check for
14    // new mail takes longer than 30 seconds to respond, we wouldn't want the
15    // next Ajax call to kick off, because it might duplicate messages (depending
16    // on the server side code).
17    // So, by using the Ajax Queue plug-in, we can ensure that our Web client
18    // is only checking for new mail once, and will never overlap itself.
19 
20     $.ajaxQueue({
21          url: "check_for_mail.jsp",
22          success: function(data)
23          {
24            var message = eval('(' + data + ')');
25            if (message.id != 0)
26            {
27              var row = newRow.replace("?", message.id);
28              row = row.replace("?", message.id);
29              row = row.replace("?", message.to);
30              row = row.replace("?", message.from);
31              row = row.replace("?", message.subject);
32              row = row.replace("?", message.sentTime);
33              $("#mailtable tbody").prepend(row);
34              $("#mailtable #"+message.id).addClass("mail_unread").addClass("messageRow");
35              $("#mailtable #"+message.id+ " td").addClass("mail");
36              $("#mailtable :checkbox").addClass("selectable");
37             }
38           }
39           });

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

第二个示例 Web 应用程序

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


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

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

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


清单 8. 生成 401k 小部件的 HTML

 1 <p><table width=300 class="percentSort" cellpadding=0 cellspacing=0>
 2 <tbody>
 3   <tr><td>S&P 500 Index</td>
 4   <td><input type=text> %</td></tr>
 5   <tr><td>Russell 2000 Index</td>
 6   <td><input type=text> %</td></tr>
 7   <tr><td>MSCI International Index</td>
 8   <td><input type=text> %</td></tr>
 9   <tr><td>MSCI Emerging Market Index</td>
10   <td><input type=text> %</td></tr>
11   <tr><td>REIT Index</td>
12   <td><input type=text> %</td></tr>
13 </tbody>
14 <tfoot>
15 </tfoot>
16 </table>

用 jQuery 设置小部件

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

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


清单 9. jQuery 小部件代码
 1 $(document).ready(function() {
 2 
 3   // the first step is to find all the tables on the page with
 4   // a class of percentSort.  These are all the tables we want to
 5   // convert into our widget.
 6   // After we find them, we need to loop through them and take some
 7   // actions on them
 8   // At the conclusion of this block of code, each table that's going to
 9   // be a percentSort widget will have been transformed
10 
11   $("table.percentSort").each(function(i){
12 
13      // each table needs a unique ID, for namespace issues (discussed later)
14      // we can simply create a uniqueID from the loop counter
15 
16      $(this).attr("id""percentSort-"+i);
17 
18      // within each table, let's highlight every other row in the table, to
19      // give it that "zebra" look
20 
21      $(this).find("tbody > tr").filter(":odd").addClass("highlight");
22 
23      // because each table needs to show the "Total" to the user, let's create a new
24      // section of the table in the footer.  We'll add a row in the table footer
25      // to display the words "Total" and a span for the updated count.
26 
27      $("#"+$(this).attr("id"+ " tfoot")
28           .append("<tr><td>Total</td><td>
29           <span></span> %</td></tr>");
30 
31      // finally, let's add the CLASS of "percentTotal" to the span we just
32      // created above.  We'll use this information later to display
33      // the updated totals
34 
35      $("#"+$(this).attr("id"+ " tfoot span").addClass("percentTotal");
36   });
37 
38   // now the second step, after we've completed setting up the tables themselves
39   // is to set up the individual table rows.
40   // We can similarly sort through each of them, taking the appropriate actions
41   // on each of them in turn.
42   // Upon completion of this block of code, each row in each table will be
43   // transformed for our widget
44 
45   $("table.percentSort tbody > tr").each(function(i){
46 
47      // get the namespace (to be discussed in the next section)
48 
49      var NAMESPACE = $(this).parents("table.percentSort").attr("id");
50 
51      // attach a unique ID to this row.  We can use the loop counter
52      // to ensure the ID is unique on the page (which is a must on every page)
53 
54      $(this).attr("id""row"+i);
55 
56      // now, within this row of the table, we need to find the text input, because
57      // we need to attach a class to them.  We utilize the namespace, and also
58      // find the :text within the table row, and then attach the correct class
59 
60      $("#"+$(this).attr("id"+ " :text").addClass("percent");
61 
62      // Finally, we attach a unique ID to each of the text inputs, and we do this by
63      // making it a combination of the table name and the row name.
64 
65      $("#"+$(this).attr("id"+ " .percent").
66                attr("id", NAMESPACE + "-" + $(this).attr("id"));
67   });
68 
69 
70   // Finally, because we know we only want numerical inputs, we restrict the text entry
71   // to just numbers.  We must do this now, because up until this point, the page
72   // contained no elements with the CLASS "percent"
73   $(".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”。下面是许多生成投资选项的行,以及后面带百分号的文本输入框。

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

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

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

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


清单 10. 名称空间解决办法
 1 // our event is attached to EVERY input text field with a CLASS of "percent"
 2 // this makes our code look good, but can lead to namespace issues
 3 $("table.percentSort input.percent").keyup(function(){
 4 
 5    // this simple line can establish a namespace for us, by getting the unique
 6    // ID attached to our table (the table is considered the "container" for
 7    // our widget.  It could be anything, depending on your specific code.)
 8    // We pass the CLASS of our widget to the parents() function, because
 9    // that effectively encapsulates the widget
10 
11    var NAMESPACE = "#" + $(this).parents("table.percentSort").attr("id");
12 
13    // with the namespace established, we can use the jQuery selection
14    // syntax to use this namespace as our prefix for all of our remaining
15    // searches.  Notice how the ambiguity is removed for our search
16    // by CLASS of "percent" and "percentTotal"
17    // This solves our namespace issues
18 
19    var sum = $(NAMESPACE + " input.percent").sum();
20    var totalField = $(NAMESPACE + " .percentTotal");
21 

因此,仅需添加一行代码,您就能够封装小部件,从而避免它的函数与自身的其他实例重复(或者甚至是其他碰巧对 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 应用程序!

posted on 2009-09-08 15:41 CONAN 阅读(313) 评论(0)  编辑  收藏 所属分类: JQuery