目前的 Web 应用开发基本上都是围绕富互联网应用( Rich Internet Application ,RIA)展开。RIA 的实现技术有很多种: Ajax 、 Flash 、 JavaFX 和 Sliverlight 等。Ajax 技术的优点在于它是构建在开放标准之上,不存在厂商锁定的问题;同时也不需要额外的浏览器插件支持。Ajax 应用对搜索引擎也比较友好。对开发者来说,Ajax 所需技术的学习曲线也较平滑,容易上手。本文简要介绍了 Ajax 应用开发的各个方面以及相关的最佳实践,但对一些细节内容没有展开讨论。
Ajax 简介
Ajax 技术的出发点在于改变传统 Web 应用使用时的“操作 - 等待页面加载 - 操作”的用户交互模式。这种交互模式会打断用户正常的使用流程,降低用户的工作效率。Ajax 技术的交互模式是“操作 - 操作 - 操作”。用户并不需要显式地等待页面重新加载完成,而是可以不断地与页面进行交互。页面上的某个局部会动态刷新来给用户提供反馈。整个交互过程更加平滑和顺畅。这是 Ajax 技术得以流行的一个重要原因。
Ajax 构建于一系列标准技术之上,包括 HTML 、 JavaScript 和 CSS 等。在这些技术中,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 过渡型,即。在编写 HTML 文档的时候,需要使用合适的元素。HTML 规范中的一些元素,如、、、
、 和等,对开发人员来说比较陌生。但是这些元素富含语义,有适合它们使用的场景。如果使用的是 和等通用元素,需要使用富含语义的 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 库提供的支持来绑定事件处理方法,可以避免这些不一致。
在绑定事件处理方法的时候,可以利用事件的传播机制来减少事件监听器的数量。如当需要为一系列
元素添加鼠标点击的事件时,可以把该事件添加到其父节点上。在完成对事件的处理之后,可以终止事件的传播,还可以阻止浏览器的默认行为。
选用合适的 JavaScript 框架
目前存在非常多的 JavaScript 框架,有开源的也有商业的。比较流行的有 jQuery 、 Dojo 、 YUI 、 ExtJs 、 MooTools 和 Prototype 等。选用流行框架的好处是有比较大的社区支持,遇到问题的时候容易获得帮助。流行框架的文档和示例也比较丰富。使用不同的框架会给应用带来不同的实现风格。jQuery 的使用者对方法的级联情有独钟,Dojo 的爱好者则倾向于把页面上的不同部分划分成 dijit 来实现。
选用什么样的框架的因素很多,技术的和非技术的都有。一般来说,轻量级的框架,如 jQuery 和 Prototype,上手比较容易,但是可复用的组件较少;而比较庞大的框架,如 Dojo 和 ExtJs,则学习曲线较陡,但是可复用的组件非常多,适合快速开发复杂的 Ajax 应用。
构建过程
Ajax 应用也需要一个完整的构建过程。构建过程的主要目的是提高 Ajax 应用的质量和性能。这个构建过程可以包含的步骤有:
- JavaScript 代码的潜在错误和代码风格检查。通过集成 JSLint 可以找到代码中潜在的问题。
- JavaScript 文件的合并、缩减和混淆。通过合并可以把多个 JavaScript 文件合成一个,减少页面加载时的 HTTP 请求个数;通过缩减可以去掉 JavaScript 代码中多余的空白字符和注释等,从而减少文件大小,降低下载时间;通过混淆则是可以替换有意义的变量名称,从而进一步减少文件大小,同时在一定程度上保护代码免被反向工程。可以执行这些操作的工具有很多,Apache Ant 就可以完成合并, JSMin 和 YUI Compressor 可以完成文件的缩减, Dojo Shrinksafe 可以进行混淆。
- CSS 文件的合并和缩减。与 JavaScript 类似,CSS 文件也可以执行同样的合并和缩减操作,从而减少 HTTP 请求数目和文件大小。YUI Compressor 可以完成 CSS 的缩减。
- 图片文件的压缩。通过对图片文件进行格式转换和压缩,可以在不损失质量的前提下,减少图片文件的大小。
测试
Ajax 应用的测试包含服务器端和浏览器端两部分。对于服务器端来说,测试的技术和工具都已经比较成熟。只需要根据服务器端采用的技术来进行选择即可。一个比较重要的原则是服务器端和浏览器端尽量实行松散耦合,以方便测试。从这个角度出发,服务器端返回数据,而不是 HTML 片段是更好的做法。可以通过工具来测试服务器端返回的结果是否正确。
浏览器端的测试目前情况不是非常理想。已经有一些单元测试的框架,如 QUnit , Dojo D.O.H 等,也存在一些集成测试的工具,如 DOH robot 和 Selenium 等。就单元测试来说,目前对仅用 JavaScript 实现的纯逻辑代码较容易实现,而对于包含了与页面上节点交互的代码则较难实现。不管是单元测试还是集成测试,目前自动化程度都不是很高。
为了便于测试,Ajax 应用中各部分之间的耦合应尽可能的小。事件处理方法的方法体应尽可能的简单。
调试
Ajax 应用的调试一直是一个比较麻烦的问题,其主要原因是不同浏览器之间存在着各种各样的兼容性问题,同一浏览器的不同版本之间也会存在很多不同。为了在所支持的浏览器上达到一致的效果,开发人员往往费劲了周折。目前的情况要好了不少,不同的浏览器都有了自己比较好用的调试工具,如 Firefox 上的 Firebug,IE 上的 developer toolbar 等。当出现问题的时候,可以通过这些工具来直接修改页面上的 DOM 结构和 CSS 样式来进行试验。找到正确的解法之后再用代码来实现。很多工具都支持直接在控制台输入 JavaScript 语句来执行,通过这种方式可以快速的查看程序中变量的值以及调用 JavaScript 方法来改变应用的内部状态,从而发现问题的原因。
内存泄露
Web 应用内存泄露的问题一直存在,Ajax 应用的出现把这个问题进一步暴露出来。目前很多的 Ajax 应用都是单页面应用( Single-page Application ,SPA)。用户通常会在单个页面上使用比较长的时间而不关闭浏览器。在用户操作过程中产生的一些小的内存泄露会累积起来,导致浏览器占用内存不断增加,应用运行起来越来越缓慢。
面对内存泄露问题,一般来说需要注意下面几点:
- 熟悉常见的内存泄露模式。最典型的是由于错误使用闭包造成的包含 DOM 节点的循环引用。打断循环引用就可以解决此问题。
- 很大一部分内存泄露与 DOM 节点相关。尽量不要为 DOM 节点对象添加额外的属性,尤其是 JavaScript 方法。
- 当内存泄露发生的时候,使用 Drip 等工具来找到发生泄露的节点并修正。
安全
Ajax 的出现并没有解决存在的一些安全问题,同时也带来了一些新的安全隐患。传统 Web 应用中存在的跨站点脚本攻击(XSS)、SQL 注入和跨站点请求伪造(CSRF)等安全问题在 Ajax 应用中仍然需要解决。对于 XSS 来说,一般的解决办法是不信任用户的任何输入。输出的时候对所有的东西进行转义(escape)。只对那些明确知道是安全的(白名单)的东西恢复转义(unescape)。对于 CSRF 的解决办法是对所有的请求添加一个验证令牌,用来确保请求是来自于自己的站点。
Ajax 带来的新的安全隐患主要与 JSON 有关。一部分 Ajax 应用的服务器端暴露 JSON 格式的数据。JSONP 允许通过
评论