Ajax实战( Ajax in action中文版 1.1为什么需要Ajax富客户端 建造一个富客户端[2]毫无疑问要比设计一个网页复杂。付出这些额外的努力,动机何在? 需要付出什么代价?而且……等一下,富客户端到底是什么? 富客户端的两个要点是:第一,它是“富”的:第二,它是“客户端”。 这好像是一句废话,别急,待我稍作解释。“富”是指客户端的交互模型,要有多样化的输 入方式和符合直觉的及时反馈手段。说简单点儿,一个“富”的应用使用起来应该像是在使 用现在的桌面应用一样,例如,就像是使用字处理软件(Word)或电子表格软件(Excl) 接下来,我们有必要仔细地考察一下所要涉及的各个方面。 1.1.1比较用户体验 花几分钟使用一下你选中的应用(浏览器除外),记下它用到了哪些用户交互,然后马上回 来。为了简短起见,我举一个电子表格的例子,但是,这里所涉及的要点是通用的,足以针 对文本编辑器上的各种情形 好,我们开始。先在电子表格中随便输入几个等式,注意到,可以以几种方式进行交互:编 辑数据,用键盘和鼠标浏览数据,还可以使用鼠标拖拽来重新组织数据。 我做这些操作的时候,程序给了我反馈。移动鼠标的时候,光标改变了形状:当鼠标停在上 面的时候,按钮变亮了;选中的文字也改变了颜色。窗口或者对话框被选中的时候,也和平 常显得不一样了,等等(图1-1)。这些就是所谓“富”的交互。当然了,仍然有一些有待 改进的地方,但这是一个好的开始 电子表格就是一个富客户端程序了吗?当然不是 在电子表格或者类似的桌面应用中,业务逻辑和数据模型是在一个封闭的环 境中运行的。在这个环境中,它们彼此清晰地了解对方,并且可以互相访问,而环境之外的 东西,对于它们来说是未知的(图1-2)。那么客户端又是什么呢?它是与另一个独立的进 程相互通信的程序,后者通常运行在服务器上。一般来说,服务器总是要比客户端大一些 能力强一些,配置更好一些,因为在服务器上通常要存储浩如烟海的信息。客户端程序使得 最终用户可以查看和修改这些信息,当多个客户端连接在同一个服务器上的时候,可以在它 们之间共享这些信息。图1-3展示了一个简单的客户服务器架构
Ajax 实战(Ajax in action 中文版) 1.1 为什么需要 Ajax 富客户端? 建造一个富客户端[2]毫无疑问要比设计一个网页复杂。付出这些额外的努力,动机何在? 需要付出什么代价?而且……等一下,富客户端到底是什么? 富客户端的两个要点是:第一,它是“富”的;第二,它是“客户端”。 这好像是一句废话,别急,待我稍作解释。“富”是指客户端的交互模型,要有多样化的输 入方式和符合直觉的及时反馈手段。说简单点儿,一个“富”的应用使用起来应该像是在使 用现在的桌面应用一样,例如,就像是使用字处理软件(Word)或电子表格软件(Excel)。 接下来,我们有必要仔细地考察一下所要涉及的各个方面。 1.1.1 比较用户体验 花几分钟使用一下你选中的应用(浏览器除外),记下它用到了哪些用户交互,然后马上回 来。为了简短起见,我举一个电子表格的例子,但是,这里所涉及的要点是通用的,足以针 对文本编辑器上的各种情形。 好,我们开始。先在电子表格中随便输入几个等式,注意到,可以以几种方式进行交互:编 辑数据,用键盘和鼠标浏览数据,还可以使用鼠标拖拽来重新组织数据。 我做这些操作的时候,程序给了我反馈。移动鼠标的时候,光标改变了形状;当鼠标停在上 面的时候,按钮变亮了;选中的文字也改变了颜色。窗口或者对话框被选中的时候,也和平 常显得不一样了,等等(图 1-1)。这些就是所谓“富”的交互。当然了,仍然有一些有待 改进的地方,但这是一个好的开始。 OK,电子表格就是一个富客户端程序了吗?当然不是。 在电子表格或者类似的桌面应用中,业务逻辑和数据模型是在一个封闭的环 境中运行的。在这个环境中,它们彼此清晰地了解对方,并且可以互相访问,而环境之外的 东西,对于它们来说是未知的(图 1-2)。那么客户端又是什么呢?它是与另一个独立的进 程相互通信的程序,后者通常运行在服务器上。一般来说,服务器总是要比客户端大一些, 能力强一些,配置更好一些,因为在服务器上通常要存储浩如烟海的信息。客户端程序使得 最终用户可以查看和修改这些信息,当多个客户端连接在同一个服务器上的时候,可以在它 们之间共享这些信息。图 1-3 展示了一个简单的客户/服务器架构
Eile Edit yew nsert Format Tools Data window He 日品 m→%4日出|口回 出sh/h/h山 图1-1这个桌面电子表格应用展现了用户交互的众多可能性。被选中单元格的行列标题都 是突出显示的:按钮在鼠标移上去的时候会显示提示信息;工具栏上排列着各种丰富的控件; 单元格也可以交互地查看和编辑 进程 进程2 逻辑 逻辑 数据 模型 文佯统 图1-2一个独立的桌面应用的架构示意图。应用运行在它自己的独立进程之 中,数据模型和程序逻辑能够彼此“看到”对方的存在。除了通过文件系统之外,同一个应 用的第二个运行实例[3]没有办法访问到第一个运行实例的数据模型。通常来说,整个程序 的状态都保存在单个文件中,当应用运行的时候,这个文件会被加锁以阻止其他的进程同时 访问
图 1-1 这个桌面电子表格应用展现了用户交互的众多可能性。被选中单元格的行列标题都 是突出显示的;按钮在鼠标移上去的时候会显示提示信息;工具栏上排列着各种丰富的控件; 单元格也可以交互地查看和编辑 图 1-2 一个独立的桌面应用的架构示意图。应用运行在它自己的独立进程之 中,数据模型和程序逻辑能够彼此“看到”对方的存在。除了通过文件系统之外,同一个应 用的第二个运行实例[3]没有办法访问到第一个运行实例的数据模型。通常来说,整个程序 的状态都保存在单个文件中,当应用运行的时候,这个文件会被加锁以阻止其他的进程同时 访问
进1 @ 见 务番 务器(例如数据庳 康务群 因持网忠务 图1-3客户服务器系统和n层架构的示意图。服务器提供了共享的数据模型,客户端与该 数据模型交互。客户端同时还维护数据模型的一部分,以获得快速的访问,但是它将服务器 端的模型当作业务领域对象的最终表示。多个客户端可以与同一个服务器交互,当然,这需 要有合适的资源锁定机制和合理的对象(或者数据行)隔离措施作为保证。服务器可以是单 进程的,就像在20世纪90年代早期和中期传统的客户/服务器模型中一样,也可以是由很 多个中间件层或者多个Web服务等组成。在任何一种情况下,从客户端的角度来看,服务 器都有一个单独的接入点,可以看作是一个黑盒 在现代的n层架构中,服务器往往要和更远的后端服务器(例如数据库)通信,因此被称作 中间件”的层同时扮演着客户端和服务器的角色。我们的Aax应用位于这个链的一端, 它仅仅是作为客户端,因此为讨论方便,我们可以把整个n层系统看作是一个标记为“服务 器”的黑盒 我的电子表格应用只需要管理它自己保存在内存或本地文件系统中的少量数据。如果架构设 计良好的话,数据和它的表现形式的耦合可以非常松散,但是我不能通过网络来分割或者通 过网络来共享它们。从这个意义上来说,电子表格应用不是一个客户端 与之相对应的web浏览器就是一个典型的客户端,它与Web服务器通信,请求需要的页面 浏览器有丰富的功能,用来管理用户的浏览行为,常见功能有回退按钮、历史列表和分页浏 览多个文档等等。但是当我们把特定网站的Web页面看作是一个应用时,这些通用的浏览 功能实际上和应用关系不大,充其量也就如电子表格和 Windows的开始按钮或者窗口列表 之间的关系。 我们来考察一下现代的Web应用。为了简单起见,我们选择了“地球人都知道”的在线书 店 Amazon com(图1-4)。在浏览器中打开 Amazon网站,因为在此之前我访问过,它会给 我显示一个友好的问候、一些推荐书目,还有我的购买历史信息。 点击推荐书目中的任何一条,就会转到另外一个页面(此时,页面要刷新一下,在这几秒钟 内我什么也看不到)。新页面是该书的相关信息:书评、二手书报价、同一作者的其他著作, 以及以前我浏览过的其他书籍(图1-5)
图 1-3 客户/服务器系统和 n 层架构的示意图。服务器提供了共享的数据模型,客户端与该 数据模型交互。客户端同时还维护数据模型的一部分,以获得快速的访问,但是它将服务器 端的模型当作业务领域对象的最终表示。多个客户端可以与同一个服务器交互,当然,这需 要有合适的资源锁定机制和合理的对象(或者数据行)隔离措施作为保证。服务器可以是单 进程的,就像在 20 世纪 90 年代早期和中期传统的客户/服务器模型中一样,也可以是由很 多个中间件层或者多个 Web 服务等组成。在任何一种情况下,从客户端的角度来看,服务 器都有一个单独的接入点,可以看作是一个黑盒 在现代的 n 层架构中,服务器往往要和更远的后端服务器(例如数据库)通信,因此被称作 “中间件”的层同时扮演着客户端和服务器的角色。我们的 Ajax 应用位于这个链的一端, 它仅仅是作为客户端,因此为讨论方便,我们可以把整个 n 层系统看作是一个标记为“服务 器”的黑盒。 我的电子表格应用只需要管理它自己保存在内存或本地文件系统中的少量数据。如果架构设 计良好的话,数据和它的表现形式的耦合可以非常松散,但是我不能通过网络来分割或者通 过网络来共享它们。从这个意义上来说,电子表格应用不是一个客户端。 与之相对应的 Web 浏览器就是一个典型的客户端,它与 Web 服务器通信,请求需要的页面。 浏览器有丰富的功能,用来管理用户的浏览行为,常见功能有回退按钮、历史列表和分页浏 览多个文档等等。但是当我们把特定网站的 Web 页面看作是一个应用时,这些通用的浏览 功能实际上和应用关系不大,充其量也就如电子表格和 Windows 的开始按钮或者窗口列表 之间的关系。 我们来考察一下现代的 Web 应用。为了简单起见,我们选择了“地球人都知道”的在线书 店 Amazon.com(图 1-4)。在浏览器中打开 Amazon 网站,因为在此之前我访问过,它会给 我显示一个友好的问候、一些推荐书目,还有我的购买历史信息。 点击推荐书目中的任何一条,就会转到另外一个页面(此时,页面要刷新一下,在这几秒钟 内我什么也看不到)。新页面是该书的相关信息:书评、二手书报价、同一作者的其他著作, 以及以前我浏览过的其他书籍(图 1-5)
amazon couk RREE;哪 Your Recommendations Haas,aIpsos tn tea 性国 Boot orignal Flower rer toyear all wih The Glasgow School of Art P的p单包建aHd M 图1-4 Amazon. com的首页。系统记得我上一次的访问。导航链接除了通用模板文件,还有 个性化信息 中中·國06 oK. C Digital Photo Deals s wt 20% off digital cameras h ur Ingalls wilder Garth w lams(straton 盈加 图1-5 Amazon com书籍详细信息的页面。包括一大堆通用信息和个性化信息的超链接。虽 说其中的大量内容和图1-4中的一模一样,但是由于Web浏览器使用基于文档的操作,每 次发送新页面都必须要重新发送这些内容 简而言之,呈现在我面前的是非常丰富的、关联度很高的信息。但是对我而言,交互的方式 就是点击那些超链接,然后填写一些表格。假设我在键盘前面不小心睡着了,第二天才醒来, 如果不刷新页面,我就没法知道《哈里·波特》系列的新书已经出版了,也不能将我的列表 从一个页面带到另一个页面,我要是想同时看到更多一些东西也不行,因为我无法改变页面 上局部内容区域的大小 我似乎是在批评 Amazon的界面,其实并非如此,我只是拿它来做个例子。事实上,在传统 Web开发方式的桎梏下,他们已经做得非常棒了。但是比起电子表格来说,它所用的交互 模型毫无疑问是太有限了 为何现代的Web应用仍然有这么多的局限呢?造成目前的状况有一些合理的技术原因,我 们现在就来考察一下 1.1.2网络延迟
图 1-4 Amazon.com 的首页。系统记得我上一次的访问。导航链接除了通用模板文件,还有 个性化信息 图 1-5 Amazon.com 书籍详细信息的页面。包括一大堆通用信息和个性化信息的超链接。虽 说其中的大量内容和图 1-4 中的一模一样,但是由于 Web 浏览器使用基于文档的操作,每 次发送新页面都必须要重新发送这些内容 简而言之,呈现在我面前的是非常丰富的、关联度很高的信息。但是对我而言,交互的方式 就是点击那些超链接,然后填写一些表格。假设我在键盘前面不小心睡着了,第二天才醒来, 如果不刷新页面,我就没法知道《哈里·波特》系列的新书已经出版了,也不能将我的列表 从一个页面带到另一个页面,我要是想同时看到更多一些东西也不行,因为我无法改变页面 上局部内容区域的大小。 我似乎是在批评 Amazon 的界面,其实并非如此,我只是拿它来做个例子。事实上,在传统 Web 开发方式的桎梏下,他们已经做得非常棒了。但是比起电子表格来说,它所用的交互 模型毫无疑问是太有限了。 为何现代的 Web 应用仍然有这么多的局限呢?造成目前的状况有一些合理的技术原因,我 们现在就来考察一下。 1.1.2 网络延迟
因特网的宏伟蓝图是将这个世界上所有的计算机都连接起来,形成一个无比巨大的计算资 源。如果能把本地调用和远程调用等同起来,那么无论是分析蛋白质的成分还是破解外太空 的信号,使用者都无需考虑机器的物理位置,剩下来的只有愉快地计算。 但是非常不幸,本地调用和远程调用是完全不同的东西。在现有的技术水平之下,网络通信 仍然是一件代价高昂的事情(也就是说,通常很慢,而且并不可靠)。在没有网络调用的情 况中,不同的方法和函数以及它们所操作的数据都位于相同的本地内存中(图1-6),向方 法内传递数据并且获得方法的返回结果是非常直接的。 模型 (本鲍内存 图1-6本地过程调用的顺序图。参与者很少,因为程序逻辑与数 据模型都保存在本地内存中,并且彼此可以直接访问 而在有远程调用的情况下,位于网络两端的通信双方为了发送和接收数据在底层需要进行大 量计算(图1-7)。比起数据在线路上的往返,这些计算需要消耗更多的时间。传输一段二 进制的数据,中间要经过很多环节的编码和解码、错误校验、失败重发、数据包拆分和重组, 数据最终转化为0和1表示的二进制信号,通过线路(或者无线连接)到达另外一方 在本地,调用函数的调用请求被编码为一个对象,然后将这个对象序列化为一系列字节,最 后使用应用层协议(通常是HIP)通过物理传输介质(例如铜缆、光纤或者无线电波)将 其发送出 在远程机器上,对应用层协议解码,将获得的数据字节反序列化,创建一个请求对象的副本 然后对数据模型应用这个对象并生成一个响应对象。为了将响应对象传递给本地的调用函 数,所有的序列化、反序列化以及传输层的操作都要反向再来一次。最后,响应对象被传递 给本地的调用函数。 本炮模型序列化》)感用协议)物理传输应用议序列化远程模型 图1-7远程过程调用的顺序图。一台机器的程序逻辑尝试操作另外一台机器上的数据模型 这个交互过程很复杂吧,幸好,它是可以自动完成的。现代的编程环境如Java和 Microsoft 的NET框架都内置了这个能力。尽管如此,执行远程调用时,上述所有这些操作仍然会在 内部执行。如果我们到处使用远程调用,性能势必会大受影响。 这也就是说,远程调用是不可能和本地调用一样有效率的。更糟糕的是,网络的不稳定更让
因特网的宏伟蓝图是将这个世界上所有的计算机都连接起来,形成一个无比巨大的计算资 源。如果能把本地调用和远程调用等同起来,那么无论是分析蛋白质的成分还是破解外太空 的信号,使用者都无需考虑机器的物理位置,剩下来的只有愉快地计算。 但是非常不幸,本地调用和远程调用是完全不同的东西。在现有的技术水平之下,网络通信 仍然是一件代价高昂的事情(也就是说,通常很慢,而且并不可靠)。在没有网络调用的情 况中,不同的方法和函数以及它们所操作的数据都位于相同的本地内存中(图 1-6),向方 法内传递数据并且获得方法的返回结果是非常直接的。 图 1-6 本地过程调用的顺序图。参与者很少,因为程序逻辑与数 据模型都保存在本地内存中,并且彼此可以直接访问 而在有远程调用的情况下,位于网络两端的通信双方为了发送和接收数据在底层需要进行大 量计算(图 1-7)。比起数据在线路上的往返,这些计算需要消耗更多的时间。传输一段二 进制的数据,中间要经过很多环节的编码和解码、错误校验、失败重发、数据包拆分和重组, 数据最终转化为 0 和 1 表示的二进制信号,通过线路(或者无线连接)到达另外一方。 在本地,调用函数的调用请求被编码为一个对象,然后将这个对象序列化为一系列字节,最 后使用应用层协议(通常是 HTTP)通过物理传输介质(例如铜缆、光纤或者无线电波)将 其发送出去。 在远程机器上,对应用层协议解码,将获得的数据字节反序列化,创建一个请求对象的副本。 然后对数据模型应用这个对象并生成一个响应对象。为了将响应对象传递给本地的调用函 数,所有的序列化、反序列化以及传输层的操作都要反向再来一次。最后,响应对象被传递 给本地的调用函数。 图 1-7 远程过程调用的顺序图。一台机器的程序逻辑尝试操作另外一台机器上的数据模型 这个交互过程很复杂吧,幸好,它是可以自动完成的。现代的编程环境如 Java 和 Microsoft 的.NET 框架都内置了这个能力。尽管如此,执行远程调用时,上述所有这些操作仍然会在 内部执行。如果我们到处使用远程调用,性能势必会大受影响。 这也就是说,远程调用是不可能和本地调用一样有效率的。更糟糕的是,网络的不稳定更让
这种效率损失捉摸不定,难以预计。相比之下,运行在本地内存之中的本地调用,在这一点 上无疑要有优势得多 等等,说了半天的远程调用,这和软件的可用性有关系吗?答案是,大有关系 个成功的计算机用户界面要能以最起码的水平模拟我们在真实世界中的体验。交互的基本 规则之一就是,当我们推一下、刺一下或者捅一下某个东西的时候,它立刻就会响应。响应 的时间只要稍微拖长一点点,就会使人困惑,分散其注意力,把关注点从手头的任务转移到 用户界面上。 远程调用横穿整个网络,需要执行大量的额外操作,它们往往会把系统拖慢,使用户察觉到 延迟。在桌面应用中,只有当可用性设计做得非常糟糕的时候,才会出现这种令用户感觉充 满bug、反应迟钝的用户界面,但在网络应用中,什么都不做就能得到大量这样的界面 因为网络延迟不可预测,这类界面问题往往都神出鬼没,对应用响应的测试也难以开展。换 句话说,网络延迟是导致实际应用的交互性糟糕的一个普遍原因。 1.1.3异步交互 用户界面的开发者对于网络延迟只能做最坏的假设。简单地说,就是要尽可能让用户界面与 网络活动无关。天才的程序员们早已发明了一种确实有效而且久经考验的方案,来专门解决 这一问题。先卖个关子,让我们到现实世界中走一趟。 在我每天早上必做的事中,很重要的一项是叫醒我的孩子去上学。我可以站在床边把他们折 腾醒,催着他们起床穿衣,但这是一种很耗费时间的方法,总要耗费我很多宝贵的早间时光 (图1-8) 唾的孩于 待哺 1.唉醒 2.慢慢醒来 3.通知 4.凝望窗外 5.态了服蘅 图1-8在我每日早晨必做的事中,以同步方式响应用户输入的顺序图。顺序图中纵向表示 时间的流逝,其中的阴影区域表示了我被阻塞不能接受其他输入的时间长度 我要叫醒孩子,看看窗外,往往会忽略了喂猫。孩子们起来之后会问我要早餐。就像服务器 端的进程一样,孩子们起床总是慢吞吞的。如果我遵循同步交互模式,就要等他们老半天ε 不过,只要他们嘟囔一句“我醒了”,我就可以先去干其他的事,需要时再回来看看他们。 按照计算机的术语,我需要做的就是为每个孩子在一个单独的线程中建立一个异步进程。开 始之后,孩子们会在他们的线程里自己起床,我这个父线程没有必要同步傻等,他们完事后 会通知我(往往还会问我要吃的)。在他们醒来的过程中,我并不需要和他们交互,就当他
这种效率损失捉摸不定,难以预计。相比之下,运行在本地内存之中的本地调用,在这一点 上无疑要有优势得多。 等等,说了半天的远程调用,这和软件的可用性有关系吗?答案是,大有关系。 一个成功的计算机用户界面要能以最起码的水平模拟我们在真实世界中的体验。交互的基本 规则之一就是,当我们推一下、刺一下或者捅一下某个东西的时候,它立刻就会响应。响应 的时间只要稍微拖长一点点,就会使人困惑,分散其注意力,把关注点从手头的任务转移到 用户界面上。 远程调用横穿整个网络,需要执行大量的额外操作,它们往往会把系统拖慢,使用户察觉到 延迟。在桌面应用中,只有当可用性设计做得非常糟糕的时候,才会出现这种令用户感觉充 满 bug、反应迟钝的用户界面,但在网络应用中,什么都不做就能得到大量这样的界面。 因为网络延迟不可预测,这类界面问题往往都神出鬼没,对应用响应的测试也难以开展。换 句话说,网络延迟是导致实际应用的交互性糟糕的一个普遍原因。 1.1.3 异步交互 用户界面的开发者对于网络延迟只能做最坏的假设。简单地说,就是要尽可能让用户界面与 网络活动无关。天才的程序员们早已发明了一种确实有效而且久经考验的方案,来专门解决 这一问题。先卖个关子,让我们到现实世界中走一趟。 在我每天早上必做的事中,很重要的一项是叫醒我的孩子去上学。我可以站在床边把他们折 腾醒,催着他们起床穿衣,但这是一种很耗费时间的方法,总要耗费我很多宝贵的早间时光 (图 1-8)。 图 1-8 在我每日早晨必做的事中,以同步方式响应用户输入的顺序图。顺序图中纵向表示 时间的流逝,其中的阴影区域表示了我被阻塞不能接受其他输入的时间长度 我要叫醒孩子,看看窗外,往往会忽略了喂猫。孩子们起来之后会问我要早餐。就像服务器 端的进程一样,孩子们起床总是慢吞吞的。如果我遵循同步交互模式,就要等他们老半天。 不过,只要他们嘟囔一句“我醒了”,我就可以先去干其他的事,需要时再回来看看他们。 按照计算机的术语,我需要做的就是为每个孩子在一个单独的线程中建立一个异步进程。开 始之后,孩子们会在他们的线程里自己起床,我这个父线程没有必要同步傻等,他们完事后 会通知我(往往还会问我要吃的)。在他们醒来的过程中,我并不需要和他们交互,就当他
们已经起来并自己穿好衣服了,因为我有理由相信他们很快会这么做的(图1-9)。 对于任何用户界面来说,这是一种沿用已久的实践,即创建异步的线程,让它在后台处理那 些需要计算很久的任务,这样用户可以继续做其他的事情。当启动这个线程的时候,有必要 阻塞用户的操作[4],但是在可接受的很短时间之后,阻塞就会解除。因为存在网络延迟 使用异步方式来处理任何耗时的远程调用是一种很好的实践。 唾的接子 紲件哺 1.啖醒 3.极醒来 4.凝望窗外 5.忘了喂葡 6.通知 图1-9以异步方式响应用户输入的顺序图。如果遵循异步的输入模式,我可以让孩子们在 醒来的时候通知我。在他们缓慢的起床过程中,我可以继续从事其他活动,这使得我被阻塞 的时间大大缩短 实际上,网络延迟问题和相关的解决方案由来已久。在老的客户/服务器模式中,当设计不 佳的客户端程序碰上了高负载的服务器时,用户界面就会出现让人难以忍受的延迟。即便是 在如今的因特网时代,当切换页面时,如果浏览器半天出不来东西,那么这种糟糕的情况很 可能就是因为网络延迟造成的。在现有技术条件下,我们暂时还没有办法消除网络延迟,但 是至少有一个对策,那就是采用异步方式处理远程调用,不是吗? 糟糕的是,对于Web开发人员而言这样做存在一个难点:HIIP协议是一个“请求一响应” 模式的协议。也就是说,客户端请求一个文档,服务器要么返回这个文档,要么告诉客户端 找不着文档或者让客户端去另外一个地方找,还可以告诉客户端可以使用它的本地缓存,诸 如此类。总而言之,“请求一响应”模式的协议是一种单向的通信协议。客户端可以向服务 器发起连接,但是服务器不可以向客户端发起连接。甚至,当客户端下次发起通信请求时, 健忘的服务器都记不起来这个客户端是谁了(HTIP是无连接的) 多数web开发者使用现代的编程语言,例如,Java、PHP或者NET,他们熟 悉用户会话(usersession)的概念,这其实是应用服务器对于不能保持连接状态的HTTP协 议的一种补救措施。HITP就其最初的设计目的来说表现得非常好,采用一些巧妙的处理, 它能够适应设计之初没有考虑的场合。但是我们的这个异步回调方案中的关键特征是,客户 端会收到两次通知,一次是在线程创建的时候,另一次是在线程结束的时候。标准的HTIP 和传统Web应用模型可不会提供这些 像 Amazon那样的传统web应用,是建造在页面概念之上的。给用户显示一个文档,上面 包括各种链接和表单,用户可进一步访问更多的文档。这种交互模式可以在很大的规模上支 持复杂的数据集(就像 Amazon和其他网站已经证明的那样),它所提供的用户体验也足以
们已经起来并自己穿好衣服了,因为我有理由相信他们很快会这么做的(图 1-9)。 对于任何用户界面来说,这是一种沿用已久的实践,即创建异步的线程,让它在后台处理那 些需要计算很久的任务,这样用户可以继续做其他的事情。当启动这个线程的时候,有必要 阻塞用户的操作[4],但是在可接受的很短时间之后,阻塞就会解除。因为存在网络延迟, 使用异步方式来处理任何耗时的远程调用是一种很好的实践。 图 1-9 以异步方式响应用户输入的顺序图。如果遵循异步的输入模式,我可以让孩子们在 醒来的时候通知我。在他们缓慢的起床过程中,我可以继续从事其他活动,这使得我被阻塞 的时间大大缩短 实际上,网络延迟问题和相关的解决方案由来已久。在老的客户/服务器模式中,当设计不 佳的客户端程序碰上了高负载的服务器时,用户界面就会出现让人难以忍受的延迟。即便是 在如今的因特网时代,当切换页面时,如果浏览器半天出不来东西,那么这种糟糕的情况很 可能就是因为网络延迟造成的。在现有技术条件下,我们暂时还没有办法消除网络延迟,但 是至少有一个对策,那就是采用异步方式处理远程调用,不是吗? 糟糕的是,对于 Web 开发人员而言这样做存在一个难点:HTTP 协议是一个“请求—响应” 模式的协议。也就是说,客户端请求一个文档,服务器要么返回这个文档,要么告诉客户端 找不着文档或者让客户端去另外一个地方找,还可以告诉客户端可以使用它的本地缓存,诸 如此类。总而言之,“请求—响应”模式的协议是一种单向的通信协议。客户端可以向服务 器发起连接,但是服务器不可以向客户端发起连接。甚至,当客户端下次发起通信请求时, 健忘的服务器都记不起来这个客户端是谁了(HTTP 是无连接的)。 多数 Web 开发者使用现代的编程语言,例如,Java、PHP 或者.NET,他们熟 悉用户会话(user session)的概念,这其实是应用服务器对于不能保持连接状态的 HTTP 协 议的一种补救措施。HTTP 就其最初的设计目的来说表现得非常好,采用一些巧妙的处理, 它能够适应设计之初没有考虑的场合。但是我们的这个异步回调方案中的关键特征是,客户 端会收到两次通知,一次是在线程创建的时候,另一次是在线程结束的时候。标准的 HTTP 和传统 Web 应用模型可不会提供这些。 像 Amazon 那样的传统 Web 应用,是建造在页面概念之上的。给用户显示一个文档,上面 包括各种链接和表单,用户可进一步访问更多的文档。这种交互模式可以在很大的规模上支 持复杂的数据集(就像 Amazon 和其他网站已经证明的那样),它所提供的用户体验也足以
满足开展业务的需要 十年来,这种交互模式在我们对因特网商业应用的看法上打下了深深的烙印。界面友好的所 见即所得( WYSIWYG)web制作工具使得站点更容易被理解为一堆页面。服务器端的Web 框架使用状态图来对页面的转换建模。没有引入,传统的Web应用就这么一直牢牢地束缚 在页面刷新操作之上,就好像这种刷新是理所当然而且无可避免的,从没有尝试过任何异步 的处理方案 当然,毫无疑问,传统的Web应用肯定不是一无是处的。毕竞 Amazon在这种交互模式上 创造了成功的商业应用。但是这种适用于 Amazon的方式并不一定适用于所有的人。为什么 这么说呢?要理解这一点,我们需要考察用户的使用模式( usage pattern) 1.1.4独占或瞬态的使用模式 泛泛地讨论自行车和SUV(运动型轿车)孰优孰劣毫无意义。因为它们各自都有优点和缺 点一一舒适度、速度、油耗或者个人身份的象征等等。只有在特定的使用模式下讨论,这样 的比较才有意义。例如,是要在上下班高峰时段穿越市中心,还是要带上一家老小去度假, 或者只是要找个躲雨的地方。在类似这样的具体情况下,我们才能有的放矢地比较。对于用 户界面,亦复如是 软件可用性专家 Alan Cooper写了很多有关使用模式的好文章,他定义了两种主要的使用方 式:瞬态的( transient)和独占的( sovereign)[5]。瞬态应用可能每天都会偶尔使用一下, 但是总是作为次要的活动,突发性地用上一会儿。与之形成鲜明对比的是独占应用,独占应 用需要应付用户每天几个小时的持续使用。 很多应用天生就具备独占或者瞬态的性质。例如,写作用的字处理软件就是一个独占应用, 可能还会用到几个瞬态的应用,比如文件管理(文件的开启和关闭窗口中常常会嵌入这个功 能)、字典或者拼写检査工具(很多字处理程序也嵌入这些功能),还有与同事联络的电子邮 件和聊天工具。而对于软件开发者,文本编辑器、调试器或者IDE(集成开发环境)则是独 占的。 独占应用常常使用得更频繁。要知道,一个良好的用户界面应该是不可见的。 衡量使用频繁程度的一个好的标尺,是当这个用户界面发生明显的停顿时,它对于用户流程 的影响。例如,从一个目录向另一个目录移动文件要发生2秒钟的停顿,我能愉快地接受 可是如果这两秒是发生在我正饱含激情地用绘画软件创作一幅作品时,或者是我正努力地调 试一段很难对付的代码时,这肯定会让我感觉十分不爽。 Amazon是一个瞬态应用,eBay、 Google以及大多数大型的公众Web应用都是瞬态应用 自因特网诞生之日起,专家们就曾经预测传统的桌面应用面临Web应用的冲击。十年过去 了,这些都还没有发生,为什么呢?基于Web页面的方案对于瞬态应用是足够了,但是对 于独占应用却还远远不够 1.1.5忘掉Web 现代Web浏览器和它最原始的出发点(从远程服务器上获得一个文档)相比已经完全不是 码事了,它们之间就像是瑞士军刀和新石器时代的狩猎工具一样,可谓是天壤之别。各种 交互组件、脚本语言和插件,这些年来无法抑制地疯狂发展,近乎强制地一次又一次地创造 着新的浏览体验。[可以到www.webhistory.org/www.lists/wwwtalk.1993q1/0182hm瞻仰一下 浏览器的史前时代。1993年的时候, Netscape创立之前的 Marc Andreessen( Netscape的创 始人)还在游说 Tim berners-lee(web的创始人,W3C的领导者)等人,列举为HTML引 入一个图片标签的好处]。 几年以前,一些先行者就已经开始把 JavaScript当作一种严肃的编程语言来对待。但就整体 而言,更多的人仍然把它和那些假模假样的警告框以及“点击猴子赢大奖”的广告一类的小
满足开展业务的需要。 十年来,这种交互模式在我们对因特网商业应用的看法上打下了深深的烙印。界面友好的所 见即所得(WYSIWYG)Web 制作工具使得站点更容易被理解为一堆页面。服务器端的 Web 框架使用状态图来对页面的转换建模。没有引入,传统的 Web 应用就这么一直牢牢地束缚 在页面刷新操作之上,就好像这种刷新是理所当然而且无可避免的,从没有尝试过任何异步 的处理方案。 当然,毫无疑问,传统的 Web 应用肯定不是一无是处的。毕竟 Amazon 在这种交互模式上 创造了成功的商业应用。但是这种适用于 Amazon 的方式并不一定适用于所有的人。为什么 这么说呢?要理解这一点,我们需要考察用户的使用模式(usage pattern)。 1.1.4 独占或瞬态的使用模式 泛泛地讨论自行车和 SUV(运动型轿车)孰优孰劣毫无意义。因为它们各自都有优点和缺 点——舒适度、速度、油耗或者个人身份的象征等等。只有在特定的使用模式下讨论,这样 的比较才有意义。例如,是要在上下班高峰时段穿越市中心,还是要带上一家老小去度假, 或者只是要找个躲雨的地方。在类似这样的具体情况下,我们才能有的放矢地比较。对于用 户界面,亦复如是。 软件可用性专家 Alan Cooper 写了很多有关使用模式的好文章,他定义了两种主要的使用方 式:瞬态的(transient)和独占的(sovereign)[5]。瞬态应用可能每天都会偶尔使用一下, 但是总是作为次要的活动,突发性地用上一会儿。与之形成鲜明对比的是独占应用,独占应 用需要应付用户每天几个小时的持续使用。 很多应用天生就具备独占或者瞬态的性质。例如,写作用的字处理软件就是一个独占应用, 可能还会用到几个瞬态的应用,比如文件管理(文件的开启和关闭窗口中常常会嵌入这个功 能)、字典或者拼写检查工具(很多字处理程序也嵌入这些功能),还有与同事联络的电子邮 件和聊天工具。而对于软件开发者,文本编辑器、调试器或者 IDE(集成开发环境)则是独 占的。 独占应用常常使用得更频繁。要知道,一个良好的用户界面应该是不可见的。 衡量使用频繁程度的一个好的标尺,是当这个用户界面发生明显的停顿时,它对于用户流程 的影响。例如,从一个目录向另一个目录移动文件要发生 2 秒钟的停顿,我能愉快地接受; 可是如果这两秒是发生在我正饱含激情地用绘画软件创作一幅作品时,或者是我正努力地调 试一段很难对付的代码时,这肯定会让我感觉十分不爽。 Amazon 是一个瞬态应用,eBay、Google 以及大多数大型的公众 Web 应用都是瞬态应用。 自因特网诞生之日起,专家们就曾经预测传统的桌面应用面临 Web 应用的冲击。十年过去 了,这些都还没有发生,为什么呢?基于 Web 页面的方案对于瞬态应用是足够了,但是对 于独占应用却还远远不够。 1.1.5 忘掉 Web 现代 Web 浏览器和它最原始的出发点(从远程服务器上获得一个文档)相比已经完全不是 一码事了,它们之间就像是瑞士军刀和新石器时代的狩猎工具一样,可谓是天壤之别。各种 交互组件、脚本语言和插件,这些年来无法抑制地疯狂发展,近乎强制地一次又一次地创造 着新的浏览体验。[可以到 www.webhistory.org/www.lists/wwwtalk.1993q1/0182.html 瞻仰一下 浏览器的史前时代。1993 年的时候,Netscape 创立之前的 Marc Andreessen(Netscape 的创 始人)还在游说 Tim Berners-Lee(Web 的创始人,W3C 的领导者)等人,列举为 HTML 引 入一个图片标签的好处]。 几年以前,一些先行者就已经开始把 JavaScript 当作一种严肃的编程语言来对待。但就整体 而言,更多的人仍然把它和那些假模假样的警告框以及“点击猴子赢大奖”的广告一类的小
把戏联系在一起 浏览器大战导致 JavaScript成了个被误解的、病态的孩子,Ajax可以看作是他的康复中心[6] 只要适当引导,然后给它配上合适的框架, JavaScript就很有可能变成因特网的模范公民。 它能真正增强Web应用的实用性,而且不强迫用户安装额外的软件,或者逼迫用户抛弃自 己心爱的浏览器。得到广泛理解的成熟的工具可以帮助我们达成这一目标。本书后面会大量 提到的设计模式就是这样一类工具 推广和普及一项新技术,既是技术事务,也是社会行为。一旦技术已经成熟,人们还需要领 会应该如何去使用它。这一步骤常常是通过用它来做我们很熟悉的事情开始的。比如,早年 的自行车叫做“木马轮”或者“蹬行马”,靠脚使劲蹬地的反作用力来前进。随着这一技术 渐渐为大众所接受,后来的发明者们会发明出这一技术新的使用方式,给它加上踏板、刹车、 链条齿轮以及充气轮胎。每一次的发明创造,都使得自行车中马的影子越来越淡,以至于彻 底消失(图1-10) 相同的过程也发生在如今的web开发领域。Ajax背后的技术有能力将Web页面转换成某种 完全不同的新东西。早期Ajax的使用尝试使得Web页面开始变得像“木马轮”一样不伦不 类。要领悟Ajax的精髓,我们就要忘掉web的页面概念,也就是说,我们要打破这些年来 所形成的经验。就在Ajax正式命名后的这几个月,这样的事已经发生了不少。 图1-10现代自行车的发展 12Ajax的四个基本原则 我们用到的很多框架中都已经固化了基于页面的传统应用模式,同时这些应用模式也已经深 深进入了我们的思想中。我们花几分钟来揭示出哪些核心概念是我们需要重新思考的,以及 如何从Ajax的角度来重新思考。 21浏览器中的是应用而不是内容 在传统的基于页面的web应用中,浏览器扮演着哑终端[刁]的角色。它对用户处于操作流程 哪一阶段一无所知。这些信息全部都保存在服务器上,确切地说,就是在用户会话上。时至 今日,服务器端的用户会话早已是司空见惯。如果你使用Java或者NET编程,服务器端的 用户会话更是标准AP的一部分——还有 Request、 Response、MME类型—一没有了它们 简直不可想象。图1-11描绘了传统Web应用典型的生命周期 当用户登录或者以其他方式初始化一个会话时,系统会创建几个服务器端的对象。例如,电 子商务类型的网站需要创建表示购物车以及用户身份证明的对象。同时将浏览器站点的首页 呈现给用户,这个HIML标记的数据流由模板文件以及特定于该用户的数据和内容(例如 该用户最近浏览的商品列表)组成。 用户每次和服务器交互,都会获得另一个文档。在这个文档中,除了特定于该用户的数据以 外,包含的其他模板文件和数据都是相同的。浏览器总是忠实地丢弃掉老的文档,显示新的 文档,因为它是哑终端,而且也不知道还可以做些什么。 当用户选择退出或者关闭浏览器的时候,应用退出,会话消失。这个时候持久层会把用户下 次登录后需要显示的信息存储起来。Ajax则不同,它把一部分应用逻辑从服务器端移到了 浏览器端,图1-12描绘了这一情况
把戏联系在一起。 浏览器大战导致 JavaScript 成了个被误解的、病态的孩子,Ajax 可以看作是他的康复中心[6]。 只要适当引导,然后给它配上合适的框架,JavaScript 就很有可能变成因特网的模范公民。 它能真正增强 Web 应用的实用性,而且不强迫用户安装额外的软件,或者逼迫用户抛弃自 己心爱的浏览器。得到广泛理解的成熟的工具可以帮助我们达成这一目标。本书后面会大量 提到的设计模式就是这样一类工具。 推广和普及一项新技术,既是技术事务,也是社会行为。一旦技术已经成熟,人们还需要领 会应该如何去使用它。这一步骤常常是通过用它来做我们很熟悉的事情开始的。比如,早年 的自行车叫做“木马轮”或者“蹬行马”,靠脚使劲蹬地的反作用力来前进。随着这一技术 渐渐为大众所接受,后来的发明者们会发明出这一技术新的使用方式,给它加上踏板、刹车、 链条齿轮以及充气轮胎。每一次的发明创造,都使得自行车中马的影子越来越淡,以至于彻 底消失(图 1-10)。 相同的过程也发生在如今的 Web 开发领域。Ajax 背后的技术有能力将 Web 页面转换成某种 完全不同的新东西。早期 Ajax 的使用尝试使得 Web 页面开始变得像“木马轮”一样不伦不 类。要领悟 Ajax 的精髓,我们就要忘掉 Web 的页面概念,也就是说,我们要打破这些年来 所形成的经验。就在 Ajax 正式命名后的这几个月,这样的事已经发生了不少。 图 1-10 现代自行车的发展 1.2 Ajax 的四个基本原则 我们用到的很多框架中都已经固化了基于页面的传统应用模式,同时这些应用模式也已经深 深进入了我们的思想中。我们花几分钟来揭示出哪些核心概念是我们需要重新思考的,以及 如何从 Ajax 的角度来重新思考。 1.2.1 浏览器中的是应用而不是内容 在传统的基于页面的 Web 应用中,浏览器扮演着哑终端[7]的角色。它对用户处于操作流程 哪一阶段一无所知。这些信息全部都保存在服务器上,确切地说,就是在用户会话上。时至 今日,服务器端的用户会话早已是司空见惯。如果你使用 Java 或者.NET 编程,服务器端的 用户会话更是标准 API 的一部分——还有 Request、Response、MIME 类型——没有了它们 简直不可想象。图 1-11 描绘了传统 Web 应用典型的生命周期。 当用户登录或者以其他方式初始化一个会话时,系统会创建几个服务器端的对象。例如,电 子商务类型的网站需要创建表示购物车以及用户身份证明的对象。同时将浏览器站点的首页 呈现给用户,这个 HTML 标记的数据流由模板文件以及特定于该用户的数据和内容(例如 该用户最近浏览的商品列表)组成。 用户每次和服务器交互,都会获得另一个文档。在这个文档中,除了特定于该用户的数据以 外,包含的其他模板文件和数据都是相同的。浏览器总是忠实地丢弃掉老的文档,显示新的 文档,因为它是哑终端,而且也不知道还可以做些什么。 当用户选择退出或者关闭浏览器的时候,应用退出,会话消失。这个时候持久层会把用户下 次登录后需要显示的信息存储起来。Ajax 则不同,它把一部分应用逻辑从服务器端移到了 浏览器端,图 1-12 描绘了这一情况
W闪览 用产 Web页面 共享数 据模型 图1-11传统Web应用的生命周期。用户和应用会话的所有状态都保留在Web服务器上。 用户在会话中看到的是一系列的页面,每次页面切换都不可避免地要到服务器上走一个来回 图1-12Ajax应用的生命周期。用户登录后,服务器交付一个客户端应用给浏览器。这个应 用可以独立处理很多的用户交互,对于自己无法独立处理的交互,应用会以后台方式发送请 求给服务器,而不会打断用户的操作流程 用户登录的时候,服务器交付给浏览器一个复杂得多的文档,其中包含大量的 JavaScript代 码。这个文档将会在整个会话的生命周期内与用户相伴。在这一过程中,随着用户与其交互, 它的外观可能会发生相当大的变化。它知道如何响应用户的输入,能够决定对于这些请求, 是自行处理还是传递给web服务器(web服务器再去访问数据库或者其他资源),或者通过 两者结合的方式进行处理 因为这个文档在整个用户会话中都存在,所以它可以保存状态[8]。例如,购物车的内容可 以保存在浏览器中而不是服务器的会话中 务器 e2s 果户的分L应月的 效据横型 据械型 J心sa中?频案的激 客户端应用 合语 退出页而 共享嫩 怩模型 12.2服务器交付的是数据而不是内容
图 1-11 传统 Web 应用的生命周期。用户和应用会话的所有状态都保留在 Web 服务器上。 用户在会话中看到的是一系列的页面,每次页面切换都不可避免地要到服务器上走一个来回 图 1-12 Ajax 应用的生命周期。用户登录后,服务器交付一个客户端应用给浏览器。这个应 用可以独立处理很多的用户交互,对于自己无法独立处理的交互,应用会以后台方式发送请 求给服务器,而不会打断用户的操作流程 用户登录的时候,服务器交付给浏览器一个复杂得多的文档,其中包含大量的 JavaScript 代 码。这个文档将会在整个会话的生命周期内与用户相伴。在这一过程中,随着用户与其交互, 它的外观可能会发生相当大的变化。它知道如何响应用户的输入,能够决定对于这些请求, 是自行处理还是传递给 Web 服务器(Web 服务器再去访问数据库或者其他资源),或者通过 两者结合的方式进行处理。 因为这个文档在整个用户会话中都存在,所以它可以保存状态[8]。例如,购物车的内容可 以保存在浏览器中而不是服务器的会话中。 1.2.2 服务器交付的是数据而不是内容