Ajax应用开发:实践者指南

目前的Web应用开发基本上都是围绕富互联网应用(Rich Internet Application,RIA)展开。RIA的实现技术有很多种:AjaxFlashJavaFXSliverlight等。
Ajax技术的优点在于它是构建在开放标准之上,不存在厂商锁定的问题;同时也不需要额外的浏览器插件支持。Ajax应用对搜索引擎也比较友好。对开发者
来说,Ajax所需技术的学习曲线也较平滑,容易上手。本文简要介绍了Ajax应用开发的各个方面以及相关的最佳实践,但对一些细节内容没有展开讨论。

Ajax简介

Ajax
技术的出发点在于改变传统Web应用使用时的“操作-等待页面加载-操作”的用户交互模式。这种交互模式会打断用户正常的使用流程,降低用户的工作效率。
Ajax技术的交互模式是“操作-操作-操作”。用户并不需要显式地等待页面重新加载完成,而是可以不断地与页面进行交互。页面上的某个局部会动态刷新来
给用户提供反馈。整个交互过程更加平滑和顺畅。这是Ajax技术得以流行的一个重要原因。

Ajax构建于一系列标准技术之上,包括HTMLJavaScriptCSS等。在这些技术中,HTML是作为应用的骨架而存在的,展示给用户最基本的内容。CSS则为HTML表示的内容提供易于用户阅读的样式。JavaScript则为应用添加丰富的交互行为,为用户提供良好的使用体验。

Ajax技术的出现使得应用中一部分的逻辑从服务器端迁移到了浏览器端。浏览器的作用从简单的渲染页面和表单处理,上升到了处理一部分业务逻辑。

一般来说有两种类型的Ajax应用风格,一种是仅少量使用Ajax技术来适当增强用户体验(Ajax
Lite),另外一种则是大量使用Ajax技术来达到与桌面应用相似的用户体验(Ajax
Deluxe),提供诸如鼠标右键、拖拽和级联菜单等。开发人员应该根据应用的特征选用合适的风格。

浏览器兼容性

开发Ajax应用的时候要面对的一个重要问题就是浏览器兼容性。虽然Ajax技术基于HTML、JavaScript和CSS等标准技术,但是不同
的浏览器厂商对于这些标准的实现程度有着很大的差别。同一浏览器的不同版本之间也会有一些不同。新版本可能会修复旧版本上的问题,也可能会带来新的问题。
不过总体的趋势是浏览器的实现越来越向标准靠拢。

解决浏览器兼容性的第一步是确定应用要支持的浏览器种类和版本。这个决策取决于应用的目标用户和特定的应用需求。对于一般的通用Ajax应用来说,可以按照浏览器的市场份额和支持某种浏览器所需的代价来确定。雅虎提出的分级式浏览器支持(Graded Browser Support)是一个很好的参照,从其中给出的A级浏览器开始是一个不错的选择。从特定的应用需求来说,某些使用了ActiveX控件的Ajax应用就只能在IE上运行;而开发针对iPhone的应用则只需要考虑移动版WebKit就可以了。

就 Ajax应用的三个组成部分来说,HTML的兼容性问题比较少,毕竟目前主流的HTML
4.01规范已经有10年的历史了;在JavaScript方面,JavaScript语言核心部分基本上没什么问题,而文档对象模型(DOM)和浏览器
对象模型(BOM)部分的兼容性问题相对较多,这主要是因为浏览器长生对规范的支持程度不同以及各自添加了私有实现。使用一个流行的JavaScript
库就可以解决这些问题;CSS方面的兼容性目前是问题最多的,而且没有比较好的库的支持。在下面会着重介绍CSS的兼容性问题。

富含语义的HTML

HTML
语言本身上手比较简单,只是一些元素的集合,只需要了解这些元素及其属性的含义即可。这些元素既有与文档结构相关和富含语义的元素,也有与页面的展示相关
的元素。一个好的实践是只使用与文档结构相关和富含语义的元素。从HTML语言规范的历史也可以看到这个趋势。HTML语言规范的历史比较长。在HTML最初的草案HTML 2.0中,HTML只包含描述文档结构的元素。在HTML 3.2中,很多与展示相关的元素被引入进来。HTML 4.01规范试图解决这个问题,许多与展示相关的元素被标记为废弃的,不推荐使用。HTML 5更进一步,引入了更多的富含语义的元素,同时移除了一些在HTML 4.01中被废弃的元素。应用这种实践进一步划分清楚了HTML和CSS在Ajax应用中的职责。

编写HTML文档的时候首先需要选用合适的文档类型声明(DTD)。目前来说最合适的HTML 4.01过渡型
即<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">。在编写HTML文档的时候,需要使用合适的元素。HTML规范
中的一些元素,
如<em>、<strong>、<abbr>、<blockquote>、<cite>
和<code>等,对开发人员来说比较陌生。但是这些元素富含语义,有适合它们使用的场景。如果使用的是<div>
和<span>等通用元素,需要使用富含语义的class属性来增加语义,说明元素的作用。在HTML文档编写完成之后,最好使用W3C提供
的HTML文档验证器来验证文档。

CSS

CSS的语法非常简单,包含的元素也非常少,其中最主要的是样式规则集。样式规则集是一系列样式声明规则的集合。每个样式规则由选择器和声明两部分
组成。选择器用来选择文档中的元素。这些元素将被应用上与选择器对应的样式声明。CSS不同版本规范所支持的选择器类型不同,尽量使用常用并且简单的选择
器以获得更好的浏览器兼容性,如ID选择器、class选择器和元素选择器。

使用CSS的时候会遇到的一个很大的问题是浏览器兼容性。经常会遇到的情况是某种样式在A和C浏览器上应用正常,而在B浏览器上则使用不正常。等到
把B浏览器调好了之后,却发现C浏览器上显示出现错误。解决这个问题的基本原则是要首先确定几个基准的浏览器和开发基本的布局样式表。基准浏览器一般是所
要支持的浏览器中对CSS规范支持较好的浏览器。基本的布局样式表保证在基准浏览器上应用的页面布局是正确的。目前的浏览器在CSS页面布局这一块的兼容
性最差,尤其在盒模型(box model)、浮动定位等方面。而在显示样式,如字体大小和颜色等方面,则基本上没有什么问题。

接下来要做的是让基本的布局样式表在除基准浏览器外的其它浏览器上正确工作。这个时候就需要对某种版本的浏览器来应用特殊的样式,从而修正样式上的
不一致。一种做法是利用一些招数(hack)。招数是利用浏览器本身对CSS规范支持的不完善或是实现上的bug来识别浏览器并应用样式。另外一种做法是
通过JavaScript来检测当前浏览器并应用样式。招数可能会随着浏览器的版本更新而变得不可用,因此尽量少使用。

在一般Ajax应用,最常被应用招数的是IE 6。因为IE 6对CSS规范支持不完善,而且存在比较多的bug,但是IE 6的用户目前还是数量众多,还是有支持的必要。对IE浏览器应用特殊样式的更好做法是使用IE独有的条件注释

当应用所包含的CSS文件比较多的时候,开发和维护这些CSS文件就成为一件比较困难的事情。一个解决办法是把面向对象的思想引入到CSS的编写过
程中。两种重要的原则是组件化和单一职责。组件化的做法是开发出针对页面上某类元素的样式组件。这些样式组件可以在不同的页面中任意组合使用。单一职责指
的是把表示结构和外观的样式分开。与结构相关的样式包括大小和位置,外观的样式包括字体大小、颜色和背景图片等。

DOM查询与操作

DOM
操作是Ajax应用中页面动态和局部刷新的实现基础。DOM定义了文档的逻辑结构,以及对文档进行访问和操作的方式。通过DOM,开发人员可以在文档中自
由导航,也可以添加、更新和删除其中的元素和内容。通过DOM规范提供的API就可以完成对文档的查询与操作。不过DOM的原生API使用起来比较繁琐,
最好使用JavaScript库来完成查询和操作。

通过DOM操作对当前页面进行修改一般都是通过响应用户的事件而发生的。这些DOM操作中一部分是纯浏览器端实现的,另外一部分则需要服务器端的支
持。服务器端可以选择返回数据或HTML片段。返回数据的好处是传输的数据量小,易于与第三方应用集成。不足之处在于浏览器端需要额外的操作来完成展示。
浏览器端可以使用DOM操作或是模板技术来生成HTML片段。服务器端也可以通过JSP和Apache
Velocity等模板技术来生成HTML片段,并直接返回给浏览器。浏览器只需要直接使用即可。这种做法的好处是浏览器端实现简单。不足之处在于与展示
相关的逻辑同时存在于服务器端和浏览器端,不容易维护。

有一些比较好的实践可以提高DOM操作的性能。首先是使用文档片段(document
fragment)。当需要插入大量节点的时候,首先把这些节点添加到一个文档片段中,再把此文档片段添加到文档上。这样可以减少页面的重新排列。其次是
使用innerHTML来更新文档内容,速度比使用DOM API要快。最后是通过cloneNode()来创建多个结构相同的元素。

事件处理

Ajax
应用与用户的交互是通过响应用户事件的方式来完成的。浏览器负责捕获用户的行为并产生各种不同的事件,应用处理这些事件。浏览器中可以产生的事件种类比较
多。事件产生之后,会按照一定的过程在当前文档树中传播。事件所产生的节点称为目标节点。完整的事件传播流程是从文档的根节点开始向下传播到目标节点(捕
获阶段),然后再往上传播回根节点(冒泡阶段)。当事件传播到某个节点上的时候,就会触发此节点上绑定的处理方法。(IE只支持冒泡阶段。)需要注意的是
事件处理方法中this所指向的对象的值,有可能是当前节点或是window对象。通过JavaScript库提供的支持来绑定事件处理方法,可以避免这
些不一致。

在绑定事件处理方法的时候,可以利用事件的传播机制来减少事件监听器的数量。如当需要为一系列<li>元素添加鼠标点击的事件时,可以把该事件添加到其父节点<ul>上。在完成对事件的处理之后,可以终止事件的传播,还可以阻止浏览器的默认行为。

选用合适的JavaScript框架

目前存在非常多的JavaScript框架,有开源的也有商业的。比较流行的有jQueryDojoYUIExtJsMooToolsPrototype等。选用流行框架的好处是有比较大的社区支持,遇到问题的时候容易获得帮助。流行框架的文档和示例也比较丰富。使用不同的框架会给应用带来不同的实现风格。jQuery的使用者对方法的级联情有独钟,Dojo的爱好者则倾向于把页面上的不同部分划分成dijit来实现。

选用什么样的框架的因素很多,技术的和非技术的都有。一般来说,轻量级的框架,如jQuery和Prototype,上手比较容易,但是可复用的组
件较少;而比较庞大的框架,如Dojo和ExtJs,则学习曲线较陡,但是可复用的组件非常多,适合快速开发复杂的Ajax应用。

构建过程

Ajax应用也需要一个完整的构建过程。构建过程的主要目的是提高Ajax应用的质量和性能。这个构建过程可以包含的步骤有:

  1. JavaScript代码的潜在错误和代码风格检查。通过集成JSLint可以找到代码中潜在的问题。
  2. JavaScript
    文件的合并、缩减和混淆。通过合并可以把多个JavaScript文件合成一个,减少页面加载时的HTTP请求个数;通过缩减可以去掉
    JavaScript代码中多余的空白字符和注释等,从而减少文件大小,降低下载时间;通过混淆则是可以替换有意义的变量名称,从而进一步减少文件大小,
    同时在一定程度上保护代码免被反向工程。可以执行这些操作的工具有很多,Apache Ant就可以完成合并,JSMinYUI Compressor可以完成文件的缩减,Dojo Shrinksafe可以进行混淆。
  3. CSS文件的合并和缩减。与JavaScript类似,CSS文件也可以执行同样的合并和缩减操作,从而减少HTTP请求数目和文件大小。YUI Compressor可以完成CSS的缩减。
  4. 图片文件的压缩。通过对图片文件进行格式转换和压缩,可以在不损失质量的前提下,减少图片文件的大小。

测试

Ajax
应用的测试包含服务器端和浏览器端两部分。对于服务器端来说,测试的技术和工具都已经比较成熟。只需要根据服务器端采用的技术来进行选择即可。一个比较重
要的原则是服务器端和浏览器端尽量实行松散耦合,以方便测试。从这个角度出发,服务器端返回数据,而不是HTML片段是更好的做法。可以通过工具来测试服
务器端返回的结果是否正确。

浏览器端的测试目前情况不是非常理想。已经有一些单元测试的框架,如QUnit, Dojo D.O.H等,也存在一些集成测试的工具,如DOH robotSelenium等。就单元测试来说,目前对仅用JavaScript实现的纯逻辑代码较容易实现,而对于包含了与页面上节点交互的代码则较难实现。不管是单元测试还是集成测试,目前自动化程度都不是很高。

为了便于测试,Ajax应用中各部分之间的耦合应尽可能的小。事件处理方法的方法体应尽可能的简单。

调试

Ajax
应用的调试一直是一个比较麻烦的问题,其主要原因是不同浏览器之间存在着各种各样的兼容性问题,同一浏览器的不同版本之间也会存在很多不同。为了在所支持
的浏览器上达到一致的效果,开发人员往往费劲了周折。目前的情况要好了不少,不同的浏览器都有了自己比较好用的调试工具,如Firefox上的
Firebug,IE上的developer
toolbar等。当出现问题的时候,可以通过这些工具来直接修改页面上的DOM结构和CSS样式来进行试验。找到正确的解法之后再用代码来实现。很多工
具都支持直接在控制台输入JavaScript语句来执行,通过这种方式可以快速的查看程序中变量的值以及调用JavaScript方法来改变应用的内部
状态,从而发现问题的原因。

内存泄露

Web应用内存泄露的问题一直存在,Ajax应用的出现把这个问题进一步暴露出来。目前很多的Ajax应用都是单页面应用(Single-page Application,SPA)。用户通常会在单个页面上使用比较长的时间而不关闭浏览器。在用户操作过程中产生的一些小的内存泄露会累积起来,导致浏览器占用内存不断增加,应用运行起来越来越缓慢。

面对内存泄露问题,一般来说需要注意下面几点:

  1. 熟悉常见的内存泄露模式。最典型的是由于错误使用闭包造成的包含DOM节点的循环引用。打断循环引用就可以解决此问题。
  2. 很大一部分内存泄露与DOM节点相关。尽量不要为DOM节点对象添加额外的属性,尤其是JavaScript方法。
  3. 当内存泄露发生的时候,使用Drip等工具来找到发生泄露的节点并修正。

安全

Ajax
的出现并没有解决存在的一些安全问题,同时也带来了一些新的安全隐患。传统Web应用中存在的跨站点脚本攻击(XSS)、SQL注入和跨站点请求伪造
(CSRF)等安全问题在Ajax应用中仍然需要解决。对于XSS来说,一般的解决办法是不信任用户的任何输入。输出的时候对所有的东西进行转义
(escape)。只对那些明确知道是安全的(白名单)的东西恢复转义(unescape)。对于CSRF的解决办法是对所有的请求添加一个验证令牌,用
来确保请求是来自于自己的站点。

Ajax
带来的新的安全隐患主要与JSON有关。一部分Ajax应用的服务器端暴露JSON格式的数据。JSONP允许通过<script>标签来获
取数据,而不受浏览器同源策略(Same -origin
Policy)的影响。不过JSONP可能造成数据被恶意的第三方窃取。攻击者还可能通过重定义JavaScript对象方法(如Array)的方式来窃
取数据。

性能

Ajax应用的性能是一个非常重要的方面,应该在应用开始开发的第一天就把性能这个因素考虑进来,并贯穿整个开发过程。如果在开发后期才考虑性能的
话,就可能与陷入一个两难的境地。一方面应用的性能达不到用户的要求,造成用户的抱怨和流失;另一个方面为了提升性能就需要对应用已有的架构做出非常大甚
至是颠覆性的调整。
Ajax应用性能的决定因素在前端。简单来说有下面几条基本的原则:

  1. 减少与服务器端交互的次数与数据大小。这点主要是减少浏览器端发出的HTTP请求的数目和降低服务器端返回的数据内容的大小。前面提到的JavaScript和CSS文件的合并和缩减都是服务于这个目的。
  2. 页面的渐进式增强。在Ajax应用中,HTML文档所包含的内容对用户是最重要的,而CSS则帮助用户方便的查看HTML文档。因此这两
    者是要被优先加载的。JavaScript文件可以稍后加载或延迟加载。因此,在HTML文档中,对CSS文件的引用要放在文档的上面,
    即<head>元素中;而JavaScript文件的一般作为<body>元素的最后一个子节点出现。部分的
    JavaScript文件可以等到页面完全加载成功之后再延迟加载。

Google的Steve Souders在前端性能这一领域做了很多开创性的工作。他写的两本书《高性能网站》和《更快速网站》都是非常好的总结性材料,值得深入研读。

个人简介

成富,目前任职于IBM中国开发中心,参与IBM产品的开发工作。对前端开发和Dojo框架有着比较丰富的经验。对新兴的Web 2.0技术也有比较浓厚的兴趣。他的个人网站是http://www.cheng-fu.com

参考资料


感谢张凯峰对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。

This entry was posted in ASP.NET. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s