一
Ajax--
整合的力量
2005
年,伴随着
Web2.0
的东风,
Ajax
逐渐进入国内开发人员的视线,并风靡全国。这个在
2005
年
2
月份由
Adaptive Path
公司的
Jesse James Garrett
命名的综合技术,综合利用
Javascript
、
XHTML
和
CSS
、
DOM
、
XMLHttpRequest
(以下称之为
XHR
)、
XML
和
XSTL
等技术,为
Web
应用程序提供异步的
http
请求处理方式,帮助
Web
应用程序实现类似桌面应用程序的快速反应功能并提供更加优秀的用户体验效果。
从
Ajax
身上,可以很清晰的看到各种技术整合到一起之后所表现出来的非凡魅力:
ü
其使用
XHTML
和
CSS
实现标准化的呈现界面。
ü
其使用
DOM
实现动态的显示和交互。
ü
其使用
XHR
实现与服务器的异步通信。
ü
其使用
Javascript
将
XHTML
、
DOM
、
XML
、
XHR
绑定。
不过要熟练使用
Ajax
进行开发,必须对
Javascript
、
XHR
相当熟悉,这对于不少程序员来说确实是个挑战。随着
Ajax
应用和研究的深入,各式各样的开源框架、包、程序库纷纷涌现。这些程序包和框架分别从各自的角度入手,试图降低
Ajax
开发的难度,比如减少
Javascript
脚本的开发量,加快开发的速度。
就目前而言,开源
Ajax
程序包和框架主要从以下几个方面对
Ajax
进行封装和扩展:
ü
远程调用工具包
远程调用工具包是
Ajax
框架最底层的工具包,其通常使用自己的
API
封装
XHR
对象,使得调用
XHR
更加简单直观。在
XHR
之前,我们通常使用内嵌的
IFRAME
来实现无刷新页面发送
http
请求的效果。因此,这些远程调用包必须支持那些不支持
XHR
的浏览器,以提高兼容性。
类似的工具包比如
Dojo
、
Prototype
等,他们都是纯
Javascript
的,适用于任何的服务端程序语言。
DWR
、
Ajax.NET
、
JSON-RP
等则是基于代理的
Ajax
框架,其允许
Javascript
直接与后台服务器端的类实现映射,是客户端的
Javascript
可以通过他们直接访问服务器对象。
ü
UI
工具包和组件
基于底层的远程调用工具包,可以很容易的实现一些胖客户端所需要的常用功能、组件和效果,比如目录树、标签面板以及菜单。这些都是可复用的。典型的比如
Script.aculo.us
,其在
Prototype
的基础上,创建了大量不同类型的
UI
效果,比如
Opacity
(不透明性)、
Movement
(移动)、
Highlight
(高显)等等。
ü
Web
应用程序工具包
这些工具包的目的在于模仿操作系统典型的
UI
效果,以创造与桌面应用程序更加接近的用户体验。典型的比如
SmartClient
,其所提供的组件能模拟
Windows
或者
Mac OSX
的
UI
效果。
ü
基于自定义标签的工具包
这些工具包利用
JSP
支持自定义标签的特点,通过自定义标签封装
Ajax
应用,使传统的
HTML
控件在必要的时候具备
Ajax
功能,比如以异步方式处理表单的提交和响应,打开超链接内容的时候不重新加载页面等等。通过这些工具包,我们能够将复杂的功能封装在
HTML
风格的特定标签中,使用
Java
代码自动处理格式化、访问外部资源等
HTML
和
Javascript
所无法处理的任务。典型的比如
Ajax Tags
。
ü
Ajax
化的
Web
框架
当前主流的
Web
框架都意识到
Ajax
的价值所在,纷纷提供对
Ajax
的支持。逐渐的,我们也可以根据自己所使用的
Web
框架,从开源项目中寻找符合要求的
Web
框架扩展。比如,
Struts Ajax Tags
就提供对
Struts
标签的扩展。
ü
代码动态生成
在
Ruby on Rails
社区,
Ruby helper
功能能够自动生成基于
Prototype
的
Javascript
代码;而
Webwork2
,则利用
Dojo
工具包,在
Java
平台上实现同样的功能。
ü
基于组件
在
.NET
领域,已经有
Ajax.NET
、
Atlas
等可复用的
Ajax
组件,其允许使用拖拉的方式在
IDE
的设计视图中快速创建包含
Ajax
功能的组件;同样的,
Java
领域的
JSF
、
Tapestry
等框架也提供了类似的组件。这些组件提供了快速开发
Ajax
应用的另一捷径。
在接下来的内容中,我们选取当前比较流行的三个开源程序包和框架,来体验一下
Ajax
的魅力。
(作者介绍:柯自聪,软件工程师,专注于
Web
应用程序开发,关注
OA
、门户、电子政务、电子商务领域,著有《
Ajax
开发精要
--
概念、案例与框架》一书以及《
Ajax
开发简略》、《
Liferay Portal
二次开发指南》等开源文档。)
二
Prototype
2.1
什么是Prototype
Prototype
是由
Sam Stephenson
开发的一个
Javascript
类库,也是其他框架的鼻祖。其对现有的部分
Javascript
对象比如
Object
、
Function
、
Dom
、
String
等进行扩展,并且对
Ajax
应用进行封装,借此提供了兼容标准的更加易于使用的类库,极大的方便开发人员快速创建具备高度交互性的
Web2.0
胖客户端应用程序。
Prototype
最初的目标是应用于
Ruby
领域的,不过由于优秀的表现和完美的封装以及服务器语言无关性,现在已经被应用到各个领域,包括
Java
、
.NET
、
PHP
等。不过在
Prototype
的源码中,还是可以找到
Ruby
的影子,比如
Ruby
样式的
Array
对象枚举。
正如之前提到的,
Prototype
是一个底层的远程调用包,虽然其仅仅是一个千余行的
Javascript
文件,但是它为其他框架提供了最底层的
Javascript
扩展和
Ajax
封装。其他
Javascript
程序库在其基础上构建了更加高级的功能和
UI
效果,比如
Script.aculo.us
。
Prototype
目前的最新版本是
1.4
,
1.5
也已经提供了
pre
版本,其官方网站提供了最新版本的下载,包括
zip
包、
js
文件和
Subvision
源码。不过和其他版本一样,
Prototype
官方网站并未提供完整的参考文档,开发者只能通过阅读源码掌握其功能。可喜的是,网上已经流传着不少关于
Prototype
源码解读和使用的文档,这在一定程度上弥补了
Prototype
官方文档不足的遗憾。
2.2
软件组织架构以及应用
Prototype
主要包括三个内容:一是提供了一些全局性的函数,替代原先烦琐重复的代码;二是对现有
Javascript
、
DOM
对象的扩展,提供访问公共函数的捷径;三是对
Ajax
应用的封装,使得开发
Ajax
应用更加容易和快速。
全局性的函数,比较有代表性的
$
系列函数和
Try.these()
函数。
$()
函数是用于替代在
DOM
中频繁使用的
document.getElementById()
方法的,其返回参数传入的
id
所指向的元素。不过,其允许传入多个
id
作为参数,然后返回一个其
id
指向的元素的
Array
对象。
$F()
函数则用于返回任何表单输入控件的值,比如文本框、文本区域、下拉列表,其也是以元素的
id
或者元素本身作为参数。不过,必须注意的是,
id
所指向的元素必须支持
value
属性,比如文本框。如果
id
指向一个按钮,那自然就得不到所要的
value
值。
$A()
函数能够将其接受到的任何可枚举列表转化成为一个
Array
对象,比如将
string
字符串转化成
Array
数组。
$H()
函数则将传入的对象转换成一个可枚举的和联合数组类似的
Hash
对象。
$R()
函数是
new ObjectRange(lowBound, upperBound, excludeBounds)
的缩写和替代。
Try.thiese()
方法以一系列的函数作为参数,按照顺序一个一个的执行,返回第一个成功执行的函数的返回值。这使得想调用不同的方法直到其中一个成功执行的需求变得容易和直观。否则我们就得变通的用
if else
去判断了。典型的比如在保证浏览器兼容的情况下实例化
XHR
对象。
Prototype
对
Javascript
的
Object
、
Number
、
Function
、
String
、
Array
、
Event
等对象进行了扩展,创建了一些新的对象和类,并在此基础上提供了很多有用的公共函数,比如
each()
、
any()
、
collect()
等。
Prototype
另外一个值得称道的是对
Ajax
的封装和简化,这也是
Prototype
吸引我们的另外一个重要之处。
Prototype
的
Ajax
功能主要由
Ajax.Request
和
Ajax.Updater
两个类完成。
在没有使用
Prototype
之前,我们需要创建
XHR
对象实例并且异步的跟踪其进程,在回调函数中使用
DOM
解析其返回的响应数据并且处理后更新页面。而
Ajax.Request
类提供了完成这一系列步骤的捷径。我们只需要将目标
URL
、
URL
参数、
http
请求方法类型、回调函数名称等一股脑的传递给
Ajax.Request
类即可。
Ajax.Request
类是针对需要解析服务器返回的响应数据的情况。而如果服务器返回的信息已经是
HTML
格式,只需要填充到某个
HTML
控件中,则可以使用
Ajax.Updater
类。其调用
innerHTML
直接将
HTML
代码填充到指定的
HTML
控件内部。
难得可贵的是,以往我们需要判断
XHR
的
readyState
和
status
值来获取
http
请求的状态并且作出相应的处理,以便应付请求失败的情况;而
AjaxRequest
和
Ajax.Updater
类提供了
onComplete
来替代这些烦琐的判断,其只需要简单的在请求的选项参数中的名为
onXXXX
属性
/
方法中提供自定义的方法对象即可。
接下来,我们使用
Prototype1.4
,列举一二,体验一下
Prototype
的主要功能及其所带来的便捷。
从
Prototype
官方网站
http://prototype.conio.net
下载最新的开发包
prototype-1.4.0.js
,放到应用程序目录中,通过
<script>
代码引入
Prototype
程序库:
<script language="javascript" type="text/javascript" src="prototype-1.4.0.js"></script>
2.3.1
$
系列函数体验
在
Prototype
出现之前,我们使用这种方式定位页面上的某个
HTML
元素及其值:
var myElement = document.getElementById(“your element’s id”);
var myValue = document.getElementById(“your element’s id”).value;
现在,可以分别使用
$()
函数和
$F()
函数来代替,例程
1
展示
$()
和
$F()
函数的用法:
var myElement = $(“your element’s id”);
var myValue = $F(“your element’s id”);
例程
1
:
$()
和
$F()
函数的用法
<p>Username:<input type="text" name="txtUsername" value="Jimmy"></p>
<p>
<input type="button" name="$Test" value=" $ " onClick="window.alert($('txtUsername'))">
<input type="button" name="$FTest" value=" $F " onClick="window.alert($F('txtUsername'))">
</p>
$A()
函数则将其接收到的可枚举的任何参数转化成为一个
Array
对象。结合
Prototype
对
Array
的扩展,
$A()
能够提供更加强大的功能。例程
2
使用
$A()
函数获取页面中的全部
input
类型的控件,并使用扩展后的
each()
函数遍历全部的控件。
例程
2
:
$A()
函数的用法
<script language="javascript" type="text/javascript">
/*$A
函数体验
*/
function do$ATest() {
var nodeList = document.getElementsByTagName("input");
var nodeArray = $A(nodeList);
var message = "
全部
input
控件:
\r\n";
nodeArray.each(
function(node) {
message += node.type + "|" + node.name + "|" + node.value + "\r\n";
}
);
window.alert(message);
}
</script>
<input type="button" name="$ATest" value=" $A " onClick="do$ATest()">
2.3.2
Try.these()
函数的妙用
我们知道,
XHR
是
Ajax
的核心之一。但是各个浏览器对
XHR
的实现不同,
IE
浏览器的各个版本对
XHR
的支持也有所差异。为了保证
Ajax
的浏览器兼容性,在实例化
XHR
对象的时候,通常要使用
try/catch
对兼容性进行判断。比如例程
3
所示。
例程
3
:使用
try/catch
块实例化
XHR
var xhr = null;
if(window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
if(xhr.overrideMimeType) xhr.overrideMimeType(“text/xml”);
}
else if(window.ActiveXObject) {
try {
xhr = new ActiveXObject(“Msxml2.XMLHTTP”);
}catch(e) {
try {
xhr = new ActiveXObject(“Microsoft.XMLHTTP”);
}catch(e){}
}
}
而现在,使用
Try.these()
函数,这些烦琐的过程变得异常简单。
例程
4
:使用
Try.these()
函数实例化
XHR
function doInitialXHR() {
return Try.these(
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')},
function() {return new XMLHttpRequest()}
) || false;
}
2.3.3
对集合类的遍历
之前提过,
Prototype
最初的应用领域是
Ruby
。
Ruby
为遍历集合中的元素提供了一系列快捷的方法,使得执行维护、查找、收集、删除其中的元素更加快速。
Prototype
新建了一个
Enumerable
对象,为
Javascript
提供类似
Ruby
的功能。
在
Ruby
、
.NET
语言中,都支持使用
each
关键词对集合中的元素进行遍历。在
Enumberable
对象中,还有很多方法比如
all()
、
any()
、
collect()
等都是基于
each()
方法实现的。所以,
each()
方法是操作集合元素的基础。
each()
方法使用
iterator
依次获取集合中的每个元素,并将其作为匿名函数的参数;也可以在该匿名函数中加上可选参数
index
,获取当前元素的索引值。其实在例程
2
中,我们已经使用了
each()
方法。
例程
5
使用
each()
方法,对一个保存货物价格的数组进行遍历,显示价格及其索引值。
例程
5
:使用
each()
方法遍历集合
function doEachTest() {
var prices = [100.2, 445, 552.3, 56.0];
prices.each(
function(price, index) {
window.alert("Value:" + price + "| Index:" + index);
}
);
}
2.3.4
P
rototype
的Ajax体验
Prototype
将
Ajax
应用封装为
Ajax.Request
和
Ajax.Update
类。使用这两个类,可以应付大部分的
Ajax
应用,而且不需要烦琐的实例化
XHR
、监控请求状态的过程。
假设我们将书籍的信息保存在一个
XML
文档中,如例程
6
所示。
例程
6
:保存书籍信息的
XML
文档
<?xml version="1.0" encoding="gb2312"?>
<books>
<book>
<title>Ajax bible</title>
<pages>500</pages>
</book>
<book>
<title>Build with Ant</title>
<pages>632</pages>
</book>
<book>
<title>Google</title>
<pages>934</pages>
</book>
</books>
现在,我们使用
Ajax.Request
类去获取这个
XML
文档的内容,并将其显示出来。例程
7
展示了这一过程。
例程
7
:使用
Ajax.Request
获取
XML
文档内容
<script language=”javascript” type=”text/javascript”>
/*Ajax.Request
类体验
*/
function doAjaxRequest() {
var url = "books.xml";
var myAjax = new Ajax.Request(
url,
{
method:"get",
onComplete:showResponse
}
);
}
/*Ajax.Request
类回调函数
*/
function showResponse(request) {
window.alert(request.responseText);
}
</script>
<input type="button" name="ajaxRequest" value="ajaxRequest" onClick="doAjaxRequest()">
图
1
展示了使用
Ajax.Request
后所获取的
books.xml
文档内容。
图
1
使用
Ajax.Request
后所获取的
books.xml
文档内容
例程
7
中,
onComplete
指定的
showResponse
函数其实是
Ajax
的回调函数,通常在这个回调函数中完成对响应数据的解析和显示。而如果服务器端返回的是已经格式化后的
HTML
代码(这点在
Ruby
中很流行),则可以使用
Ajax.Updater
。例程
8
使用
Ajax.Updater
将服务器的响应数据填充到指定的
div
中。图
2
展示了使用
Ajax.Updater
的执行效果。
例程
8
:使用
Ajax.Updater
获取服务器的响应数据
<script language=”javascript” type=”text/javascript”>
/*Ajax.Update
类体验
*/
function doAjaxUpdate() {
var url = "response.jsp";
var pars = "field=all&show=true";
var myAjax = new Ajax.Updater(
"divContent",
url,
{
method:"get",
parameters:pars
}
);
</script>
<input type="button" name="ajaxUpdate" value="ajaxUpdate" onClick="doAjaxUpdate()">
<p><div id="divContent"></div></p>
图
2
使用
Ajax.Updater
的执行效果
例程
9
是例程
8
所请求的
JSP
文件。其简单的打印出加粗后的“
Ajax.Update
”字样。
例程
9
:
<%@ page contentType="text/html; charset=gb2312"
language="java" import="java.sql.*" errorPage="" %>
<%="<strong>Ajax.Update</strong>"%>
三
Dojo
3.1
什么是Dojo
Dojo
是一个底层的
Javascript
工具包,其设计的目的是要使
Web
应用程序中增加动态特性更加容易和快捷。其提供了很多漂亮的窗口部件和
UI
效果,比如控件的浅入浅出、移动、拖拽、擦除、树菜单、内容面板、标签面板、面板容器、
Windows
窗口、向导窗口等等。
Dojo
所提供的强大的可靠的组件,可以使
Web
应用程序具备更加优秀的用户体验效果,响应更快,功能更多。
与
Prototype
相比,
Dojo
提供了更完善丰富的文档。除了完整的
Doc
文档和
API
参考,
Dojo
还提供了丰富的
Demo
示例,使得用户更真切的体会到
Dojo
的应用效果。
与
Prototype
不同的是,
Dojo
本身实现了很多
UI
效果,比如浅入浅出,而这些效果在
Prototype
是由其额外的扩展实现比如
Script.aculo.us
实现的。
Dojo
的最新版本是
0.3.1
。我们可以从其官方网站下载到最新的
zip
开发包,其中包含了丰富的
demo
示例。
3.2
软件组织架构以及应用
Dojo
是由多个包组成的,其中包括:
ü
dojo.io
:对
Ajax
应用进行封装,提供简单的
API
实现
Ajax
功能。
ü
dojo.event
:提供浏览器兼容的事件体系。
ü
dojo.lang
:用于支持混合(
mixins
)及扩展对象。
ü
dojo.graphics
:用于实现优秀的
HTML
特效功能,比如浅入浅出、移动等等。
ü
dojo.dnd
:用于支持拖拽功能。
ü
dojo.animation
:用于支持动画效果的创建。
ü
dojo.hostenv
:用于提供
Javascript
扩展,比如提供
import/includes
替换原有的
src
属性。
对于
Ajax