1 简介
当你开始建造一个Ajax应用时,你很快就会遇到一些情形,让你感觉自己好像是在重新发明轮子。
大量的开发者一想到Ajax,就会直接扑向XMLHttpRequest对象,但这仅仅只是一个开始,而且是Ajax
开发中很容易的部分。
当你在建造JavaScript富客户端应用时,你会遇到大量令人烦恼的事情。浏览器的兼容性、对于降
级(degradation)的支持、混杂在一起的DOM处理、还有那些鲜血淋淋的hack技巧(例如离线存储),
所有这些事情都会冒出来。
这篇文章将会为你介绍一个远远超出对XHR进行简单封装的工具包。它是那种每个人只要开发富客
户端的Ajax应用都应该使用的工具包。没有了它,你就是一个残废的开发者,只能依靠自己的力量来蹒
跚前行。
与其列出Dojo工具包可以使用的API,我们不如考察一个简单的应用,看看在实际开发中如何使用
这个库的各个部分。
我们将会讨论:
• Dojo是什么?
• 如何设置Dojo
• 实际使用DOM和HTML效果
• 通过dojo.io.bind()实现Ajax远程调用
• 拖放(Drag and Drop)操作
让我们开始吧!
2 Dojo工具包:JavaScript开发的水槽(Kitchen Sink)
Dojo工具包是什么?它是一个框架还是一个库?这个名字本身究竟意味着什么?Alex
Russell,Dojo工具包的创建者之一,认为它是一个你可以用来建造JavaScript应用的库。
它的内涵比你可能想到的更加丰富,因为它在一个顶级项目的一组package中包含了如此众多的功
能。
Dojo中包含有很多JavaScript编写的子package,负责处理当你在建造一个JavaScript应用时通常
必须要自己来编写的基础(infrastructure)工作。它封装了跨浏览器的令人烦恼的代码,这样你就不
必担心它们会来给你捣乱。它可以帮助你更加容易地处理DOM。它在不同的浏览器中可以将
XMLHttpRequest对象降级为使用其他的远程调用机制(译者注:例如,那些不支持XMLHttpRequest对象
的浏览器),同时为你提供了最简单的和容易使用的API。
JavaScript并非仅仅是关于浏览器的,它还可以运行在不同的宿主环境中,Dojo对此也提供了支持。
如果你想要在服务器端编写JavaScript,仍然可以使用Dojo来使得代码更加简单。
为了使你获得一个关于这个框架的感性认识,我们来列出其中的package:
• dojo.collections:很有用的集合数据结构(List、Query、Set、Stack、Dictionary...)
• dojo.crypto:实现加密功能的API(Blowfish、MD5、Rijndael、SHA...)
• dojo.date:无须编写丑陋的代码来解析日期格式。
• dojo.dnd:拖放功能的辅助API。我们将要建造一个支持拖放功能的应用。
• dojo.event:事件驱动的API,支持AOP开发,以及主题/队列的功能。
• dojo.lfx:HTML和SVG效果库。我们将在例子中使用这些效果。
• dojo.animation:基于Dan Pupius在动画方面的工作
http://pupius.co.uk/js/Toolkit.Drawing.js)的动画package(不再支持,应首
选dojo.lfx)
• dojo.fx:不再支持,应首选dojo.lfx
• dojo.io:不同的IO管道。cookie、IFrame、发布/订阅功能等等。所有神奇的Ajax工作都
在这里完成。
• dojo.lang:对于整个JavaScript环境进行增强的功能。包括你所希望拥有的很多特征,例
如mixin、基于闭包(closure)的函数,以及大量的其他功能。
• dojo.logging:提供日志功能的框架
• dojo.math:数学函数(曲线、点、矩阵)
• dojo.reflect:提供反射功能的函数库
• dojo.rpc:与后端服务(例如理解JSON语法的Web服务)进行通信
• dojo.storage:将数据保存在本地存储中(例如,在浏览器中利用Flash的本地存储来实现)
• dojo.string:现在你可以对字符串进行如下的处理,修整、转换为大写、编码、esacpe、填
充(pad)等等。
• dojo.undo:用来撤销用户操作的栈管理器
• dojo.uri:处理URI的函数库
• dojo.widget:一个widget框架,允许你建造可重用的HTML/JavaScript widget,可以与简
单的HTML标记共同使用(例如,<div class=”dojo-MyWidgetType”>)。支持基于标记的
应用开发(例如:XAML、XUL)
• dojo.xml、dojo.dom:帮助你处理DOM的辅助函数,以及其他的XML辅助函数。我们将在这
个例子中使用dojo.dom。
• dojo.style:CSS功能,例如访问style的大小、与浏览器的盒模型配合工作,以及更多的功
能。
这确实是一个很长的列表,然而这个列表仍然未能公正地体现这个框架所做的工作。如果你使用
Dojo,在开始编写一些基础的代码之前,最好先在框架中搜索一下这些功能,很可能别的某个人已经为
你完成了这些功能!
你很快会发现,如果你想要建造任何严肃的Ajax应用,Dojo能够为你提供大量的帮助。它可以帮助
你从hack少量的onclick事件,升级到编写具有专业品质的JavaScript应用。
3 设置和配置Dojo
通常你是如何设置和配置一个JavaScript库的呢?你是不是简单地将JavaScript文件拷贝过来,放
在Web服务器上,然后通过<script src=...来访问呢?我敢肯定八成你会这样说。
简单的JavaScript库就是这样来使用的,Dojo也可以这样来使用。然而,如果能够了解整个大的场
景,将能够更好地使用Dojo。
3.1 选择正确的Dojo创建
因为这个工具包有如此众多的部分,你可以在一组创建中进行选择。为何要添加这种复杂性,而不
是仅仅只提供一个下载版本呢?
在大量的环境中,只提供一个下载版本不会有任何问题,但是对于浏览器中的JavaScript而言,传
输给浏览器的每一K字节都是很要紧的(译者注:延迟会kill掉整个的使用体验)。如果你只想完成单
个的Ajax调用,为何要将全套的package都发送给浏览器呢?我们可不想无缘无故地发出额外的比特。
当你下载Dojo的一个“版本”时(从http://download.dojotookit.org/下载zip文件,或者匿名
访问Dojo的SVN服务器http://svn.dojotoolkit.org/dojo/trunk/),你可以在下列选项中进行选择:
• I/O(XHR)版本:如果所有你需要的仅仅是以Ajax的风格与服务器通信?那么请选择这个版
本。
• Event+I/O版本:I/O版本加上事件系统。
• Ajax版本:在这个版本中你不仅得到了I/O和事件功能,而且还有killer级的可视效果。
• Widget版本:如果你爱好分离CSS、内容和JavaScript,那么你可以使用这个创建来开发下
一级别(next level)的widget系统
• “Kitchen Sink”(水槽)版本:我不想考虑这个版本。如果你确实想要下载这个框架的每
一个比特并且使得你的用户接受你的选择,这个版本将会给你想要的东西。
使用水槽版本,用户将下载所有的东西,因此你无须在加入一个项目开发早期未曾预测到的package
时做任何的修改。尽管如此,仍然不建议使用这个版本。不仅仅是因为你强迫所有的用户下载大量不必
要的JavaScript代码,而且它在实际应用中确实会带来一些问题。例如,对于Dojo最近的一些版本而言,
如果你使用了水槽版本,一些用户将会看到他们的浏览器状态条永远都告诉他们“仍然在加载...”
(Still loading...)。这轻而易举地破坏了整个页面的使用体验。原因是水槽版本包括了所有的
package,特别是dojo.storage.*这个package。这个package使用Flash 8来支持本地存储,在没有安
装Flash 8的IE浏览器中会引起问题。这真是一个坏消息。
为了开始使用一个Dojo创建,简单地从你下载的创建中找到dojo.js文件,然后以正常方式加载它:
<script type="text/javascript" src="http://yoursite.com/scripts/dojo.js"></script>
3.2 动态加载package
以编程的方式请求package,使得它们可以为你所用,这不是很好的事情吗?我的意思是说,我们在其他
语言中一直都是这样做的。我们在C中使用include,在Java中使用import,在Perl中使用use,在Ruby中
使用require。
Dojo团队也想要这样做,他们将这个功能加入了平台中。如果你需要使用一个尚未下载的package,你只
需要这样做:请求这个package!
在我们的例子中,我们将请求拖放、事件系统和fx这3个package:
<script type="text/javascript" src="http://yoursite.com/scripts/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.dnd.*");
dojo.require("dojo.event.*");
dojo.require("dojo.lfx.*");
// now we are ready to work with any of these packages
</script>
对require方法的调用将会动态获取那些package所需的JavaScript代码,并且将它们加载进页面中。
你也可以编写你自己package,并且也可以加载你的名字空间。每个包中有一些元数据来告诉请求加载器它真
正需要什么。
例如,这里是包含有拖放package的元数据的__package__.js文件:
dojo.hostenv.conditionalLoadModule({
common: ["dojo.dnd.DragAndDrop"],
browser: ["dojo.dnd.HtmlDragAndDrop"]
});
dojo.hostenv.moduleLoaded("dojo.dnd.*");
你将会注意到,依赖于宿主环境,在运行时可以加载不同的源文件。如果你不是在一个浏览器中运行
Dojo,你就不需要加载HtmlDragAndDrop的源文件。
3.3 定制的创建
动态地延迟加载(lazily loading)需要的JavaScript代码是很棒的,但是开发完成之后,你可能想要
创建一个JavaScript文件,里面包含所有你的应用所需要的代码。
很幸运,Dojo有一个创建系统,可以帮助你来做这件事。你需要下载源文件,并且从这里开始工作。源
文件保存在Subversion中,代码库在:http://svn.dojotoolkit.org/dojo/trunk/(从项目的HEAD分支中获
取)。
我们可以讨论如何制作定制的创建,但是我们先将它放在一边,回到我们的应用上来。
4 应用:旅行路线编辑器
现在我们已经知道如何设置Dojo了,我们来使用它,将它的一些package使用在一个真实的应用中。这
个页面是一个旅行路线编辑器,你可以使用它来:
• 为你的旅程添加和删除天
• 在每一天中添加和删除要访问的站点。
• 在不同的天之间拖放站点。
所有的这些活动将在一个Ajax界面中完成。一个页面,很多操作。这个页面是整个过程中的第二步,在
这里用户已经选择了站点并且完成了初始的放置。
屏幕看起来像是这样:
我们来开发这些功能。
5 DOM和HTML效果
我们首先应该做什么呢?似乎应该是开发在路线中添加新的一天的功能。为了做这件事,我们需要建造新
的一天所需的HTML,包括它的标题、一个空的集合(因为还没有为它添加站点)等等。
我们能够使用DOM来建造这些,但是如果我们能够只做一个拷贝,这将变得更加容易。为了做这件事,我
们可以在HTML中加入一个空的表格,但是不显示它。
这是一组包含列表的div,模板代码的作用是作为一个占位符(例如EMPTY DAY,而不是天号的标题,以
及特殊的id,用来在程序中获取):
<div , function(e) {
dojo.fx.wipeIn(e.dragObject.domNode.id, 300);
checkEmptyDay(dl.id.substring(3));
remoteMoveSiteOntop(e.dragObject.domNode.id, e.target.id);
});
}
7.1.3 onLoad
如果你想要将一些东西加载进浏览器,在DOM加载完成后,你通常会这样设置onLoad处理函数:
<body onLoad=”doLoad()”>
或者:
window.onload = function() { … }
这在一些场合是适用的,但是如果多于一个库使用这种方法呢?你就会遇到冲突,在后面设置
window.onload处理函数的库将会胜出,而设置在前面的那个处理函数中的代码永远也不会被调用。
为了解决这个问题,你可以很方便地使用Dojo的辅助函数:
dojo.addOnLoad(function() {
enableSiteMoving();
});
addOnLoad将确保所有以这种方式添加的函数将会被排队,并且都将会被调用。
addOnLoad还有另外一种调用风格,dojo.addOnLoad(object, ‘functionName’)。这将调用传递进来的
对象上的指定的functionName。
8 结论
希望你现在已经体验了这个支持Ajax、具有拖放功能的界面,这里无需刷新页面来完成大量的编辑功能。
我们肤浅地展示了Dojo作为一个完整的工具包所能为你提供的功能。我希望你去深入考察一下这个库,
深入理解当我们在进行严肃的JavaScript开发时需要些什么支持。
如果你仅仅想要hack几个事件处理函数,Dojo也许并不适合你。然而,如果你想要开发专业级的应用,
不妨试一下Dojo,在这里丑陋烦琐的工作已经由聪明的JavaScript开发者帮你完成了。
我还建议你从Dojo的代码库中check out出widget工具包,将这些代码作为一个很棒的基于标记的开发
(markup based development)的例子。
9 作者简历
Dion是领先的Ajax社区Ajaxian.com,以及受欢迎的播客站点“Audible Ajax”的共同创始人。
他是Pragmatic出版社新出版的“Pragmatic Ajax”的合著者之一。
他是openxource.com、onjava.com、TheServerSide.com等网站企业Java主题的专栏作家,他的blog在
almaer.com/blog。他乐于为各种技术交流活动写作和发表演讲,例如JavaOne、JavaPolis、TheServerSide
座谈会,以及No Fluff Just Stuff座谈会。他是JCP专家组的成员,也是开源社区的积极参与者。

相关文章: