写点什么

Dojo:不容忽视的 RIA 框架

2010 年 12 月 09 日

受邀写这样一个专题,非常乐意。这也让我看到了 infoQ 是一个真正以技术为导向的社区,不追求最热的话题,只看技术的价值,很欣赏。

2005 年 5 月,Ajax 概念被第一次提出。而在此一年之前,Dojo 框架已经写下了第一行代码。作为 Ajax 之前的“Ajax”框架,Dojo 官网至今一直用着朴素的名字来定义自己:javascript toolkit。看上去仅仅是个工具集,而事实上它却有框架的力量,推进着大型 Web2.0 应用的开发。这也是 Dojo 一直给人的印象,低调、沉稳,却很强大。如果你仅仅想让 WordPress 页面的下拉菜单效果更加酷,我不反对用 JQuery;但如果你需要以 Web2.0 技术为基础去架构一整个应用,那么 Dojo 一定是你的最佳选择。

为什么选择 Dojo?

看完前一段,一定会有人说,Dojo 给人的印象没有强大,只有庞大,难用以及糟糕的性能。这完全可以理解。Dojo 文档的缺乏,社区的不活跃,相对严格的设计模式,让很多初学者难以上手,于是转投用起来更加友好的其它框架。这是一个偏见导致的恶性循环,造成用 Dojo 的人始终不多。而消除偏见,正是这个专栏的目的。

如果我们相信大公司 CTO 的眼光,不妨先看看 Dojo 的这些用户:

这只是 Dojo 用户的一小部分,实际用 Dojo 的公司更多,甚至中国的腾讯,百度。如果觉得这些公司的技术离自己很远,那么 Java 的 Struct 2 框架内置 Ajax 模块用的是 Dojo,也许更能说明 Dojo 的价值。

为什么选择 Dojo?这个问题其实很大,也许当你看完整个专题系列,心中自有选择。但在这里,我还是想先简单列出一些 Dojo 有吸引力的特性:

特性

描述

优秀的面向对象体系

类定义,多继承,基类方法调用,在 Dojo 中都设计的自然而合理。良好的 OO 体系,是大型应用的基础。

命名空间

和 Java 一样的命名空间管理,对应文件夹层次结构,让我们能有效管理大量源代码以及他们的依赖关系。

Dijit 界面控件系统

Dijit 是 Dojo 的最大特色,它很容易创建和使用,方便了界面模块化开发且易于维护;基于内容构建的天然特性更方便创建 SEO 优化的 Ajax 页面。

严格的设计模式

很多人说 Dojo 难用,严格的设计模式是一个重要原因。很多东西也许第一眼看上去不是很直观,但一但掌握,用这种风格写出的代码会非常容易扩展和维护。

灵活的单元测试框架

DOH 是 Dojo 自带的测试框架,除了标准的函数级别单元测试,你还能用它自动控制鼠标移动和点击来进行自动的界面测试。而且 DOH 同样可以用于非 Dojo 框架的其它 Ajax 测试。

强大的构建工具

Dojo 的 Build 工具其实有很多限制,需要遵循一定的编码规范,这为丰富的功能提供了前提。它能自动处理文件依赖关系,自动分离 i18n 文件,自动嵌入模板文件,让交付的系统运行的更快。

性能其实很优秀

性能很难对比,即使相同的一个 Calendar 控件,功能都会有差别。这里从 2 个点来看性能问题:DOM 节点的查询速度,以及 Dijit 展现的速度。前者大家的功能完全相同,可以有个比较;后者则专属 Dojo,看看绝对的性能。

(1)节点查询速度

JQuery 最早引入基于 CSS 选择器进行 DOM 节点查找的机制,也是其最大特征。如今成为了各个框架都具备的功能。这应该算是最能体现 Javascript 算法设计的一点。下图来自于: http://blog.creonfx.com/javascript/mootools-vs-jquery-vs-prototype-vs-yui-vs-dojo-comparison-revised

是各个框架查询速度的对比,时间越少表示性能越好。虽然用的版本比较老,但在查询的实现上各个框架都没有太大的变化。

一图胜千言,Dojo 的性能优势一目了然。如果你想亲自做这个测试,可以访问如下地址。

http://mootools.net/slickspeed/

(2)Dijit 展现速度

Dijit 是 Dojo 的界面展现体系,性能好坏直接决定着页面的响应速度。在声明方式下,Dojo 需要遍历页面 Html 找到所有的 Dijit 进而创建并展现它们。这个过程比你的想像要快的多,Dojo 的 ThemeTester 是一个很好的例子:

它的访问地址是: http://archive.dojotoolkit.org/nightly/dojotoolkit/dijit/themes/themeTester.html

此页面包含了399 个Dijit,全部通过声明方式创建。在FireFox3.6 下总共的展现时间是2000ms 左右,平均一个Dijit 用时5ms。页面中包括了多个Tree,多个TabContainer,多个Menu 以及富文本编辑器这样的复杂Dijit。因此,如果你发现你的界面远没有这么复杂,却展现的很慢,通常需要检查自定义的Dijit 设计的是否合理。

综上所述,Dojo 本身,包括自带的Dijit,拥有着相当不俗的性能。至于如何写出高性能Dijit,正如如何写出高性能的JQuery 插件,需要的是经验和积累。

性能是软件开发的一个永恒话题,无处不在。没有一个统一的解决模式,桌面程序也好,Web 程序也好,要提高性能,合理的设计、高效的算法永远是解决问题的王道。而Dojo 在这一点上,绝不会是你的绊脚石。

开始使用Dojo

作为专栏的开篇,这里并不会详细介绍很多具体的技术细节。而是通过几个重要Dojo 相关网站的介绍来了解Dojo,如下表所示:

网站

介绍

Dojo 官网

本质上官网总是没有太多实际性的作用,作为一个产品门户,起到总体介绍的作用,内容相对很少,而且个人觉得 Dojo 官网的设计实在差强人意,很多过期内容,而且导航非常不便。

Dojo 下载地址

在这里可以下载到所有的 Dojo 稳定版本,每个版本都有几个不同的包可供下载,以 dojo1.5 为例:

  1. dojo-release-1.5.0.zip
    Dojo 标准包,已经压缩,可供生产环境直接使用。
  2. dojo-release-1.5.0-src.zip
    Dojo 源代码,包含测试,build 工具,开发环境使用,方便调试。另外这个包下面包含了所有的 test case,通过它们能快速掌握各个控件的用法。
  3. dojo-release-1.5.0-docs.zip
    Dojo 离线文档,内容与 http://api.dojotoolkit.org 基本相同,但没有在线的使用方便,一般直接看在线文档即可。
  4. dojo-release-1.5.0-demos.zip
    Dojo 功能演示,其实这是非常直观的一块,可以看到很多很酷的用法,每个 Demo 都有具体的代码示例。下载解压后放到 dojo 同级目录即可使用。
  5. dojo-release-1.5.0-shrinksafe.zip
    Dojo 的 build 工具,通常专用于 Dojo,已经包含在 src 包里,如果要用于其它的 Javascript 程序打包,可单独下载。

在线API 手册

经过改版,最近刚刚推出。相对于旧版本已经方便很多。可以查到所有类的属性,方法,事件的介绍和使用示例。

Dojo Campus

在这里能找到几乎所有的类或方法的使用 guide,相当于 Dojo 的 Wiki,内容不断更新。通常在 google 中搜索 Dojo API,来自 Dojo Campus 的是最有价值的。

Sitepen

这是来自 Dojo 创建者的博客,里面会有 Dojo 相关的技术文章,也会有业界的一些观点文章。

Dojo 中文博客

近期比较活跃的 Dojo 中文博客,包括原创内容以及国外 Dojo 技术文章翻译。翻译部分基本会同步于 Sitepen 的文章。

捷径:从 Dojo Test Case 学习 Dojo 控件用法

除了上述网站之外,有一个快捷学习使用 Dojo 的方法,就是看 Dojo Src 包下面的 test case。在源代码包下面(例如 dojo-release-1.5.0-src.zip),每个命名空间下都会有一个 tests 目录,这里面包含了每个控件或者工具类的各种用法的实例,通过它们能够快速上手它们的用法。例如:

http://yourhost/dojoroot/dijit/tests/test_Dialog.html,这个页面就展示了 Dialog 的各类用法。任何一个 Test Case 都是一个完整的 Dojo 环境,你完全可以模仿它写出自己的第一个 Dojo 程序。

从对象、事件、闭包看 dojo 对 javascript 的扩展

和任何一个 Ajax 库一样,Dojo 对 Javascript 语言本身也做了扩展,例如用 dojo.forEach 来方便的遍历一个集合。因此如果熟悉了一个框架,上手 Dojo 基本上只需要熟悉不同的 API 命名方法,dojo 的这些功能基本都在 dojo 命名空间下,通过 dojo.doSomething() 的形式来调用,或者 dojo.string.doSomething() 这样的形式。

在这个功能上我们从 3 个方面的例子来看 dojo。

(1)面向对象支持

开始写代码之前,我们先为源代码位置指定一个命名空间,用如下代码:

复制代码
dojo.registerModulePath('com.infoq', '../infoq');

第一个参数表示你的根命名空间,第二个是相对于 dojo.js 的目录。这样整个目录结构如下:

复制代码
/script
/dijit
/dojo
/dojox
/infoq

你可以注册多个命名空间到多个文件夹,方便组织源代码文件。这个结构和 java 非常类似,甚至比 java 少了 com/infoq 这样的多余目录。下面的代码就定义了一个 Dojo 类,这个类文件位于 infoq/demo/Class1.js。代码中示例了继承,基类方法调用,构造函数的使用。

复制代码
dojo.provide('infoq.demo.Class1'); //dojo 特有,表明这个文件所属命名空间
dojo.require('infoq.BaseClass'); // 引入基类定义
dojo.declare('infoq.demo.Class1', [infoq.BaseClass], { // 类定义
constructor: function(){
// 构造函数代码逻辑
}
,doSomething: function(){
// 调用基类 infoq.BaseClass 的 doSomething 方法
this.inherited(arguments);
}
});

示例中演示了 Dojo 的类定义,继承,基类方法调用的方法。可见其过程与传统面向对象的编程非常类似。Dojo 中通常一个类就是一个文件,并且文件路径和 dojo.provide 声明的类路径严格对应。

(2)事件支持

Dojo 事件系统最大的特点是统一了 DOM 节点的原生事件和自定义事件,例如:

复制代码
var handler = dojo.connect(obj, ‘onLoad', callback);

这个 onLoad 可以是 DOM 节点的原生事件,也可以是 obj 对象的一个纯 javascript 方法,调用 onLoad 的参数也会传个 callback 方法。dojo.connect 函数返回一个 handler,可以用 dojo.disconnect(handler) 的方式取消一个事件的绑定。

(3)闭包及 dojo.hitch

闭包是 javascript 的重要特色,它让函数回调的定义变的非常便捷。当年看到 Prototype 提供的 Function.bind 方法惊为天人,dojo 中也提供了同样功能的函数:dojo.hitch。假设某对象的定义如下:

复制代码
var obj = {
doSomething: function(arg1, arg2, arg3){
//code
}
};

那么要把 obj.doSomething 进行一个闭包封装可以使用:

复制代码
var callback = dojo.hitch(obj, 'doSomething', 'arg1Value');

那么执行:

复制代码
callback('arg2Value', 'arg3Value');

完全等价于:

复制代码
obj.doSomething('arg1Value', 'arg2Value', 'arg3Value');

可以看到 dojo.hitch 甚至可以连接 2 个部分的参数传递,这个功能非常有用。

忘掉 $(id),看 Dijit 中如何使用 DOM 节点

$(id) 曾经是 Prototype 框架的特色,也是大家最为津津乐道的常用函数。后来各个框架均提供并加以扩展(JQuery 中用于基于 CSS 选择器的节点选择)。dojo 中也有一样的函数:dojo.byId(id)。但与其它框架对于 $ 方法的不可或缺相比,dojo.byId 使用非常少,因为它意味着对 DOM 节点的紧密耦合。Dojo 中严格的模块化设计思想极力避免紧密耦合的出现。

开发中用到 DOM 节点时,通常和 UI 相关,而 Dojo 中所有对 UI 的操作和渲染都抽象成 Dijit 的形式,对于复杂的 UI 结构,可以嵌套使用 Dijit。创建 Dijit 有 2 种方式,一种基于已有 DOM 节点,另一种基于模板。

(1)使用已有 DOM 节点创建 Dijit

首先自己定义一个 Dijit:

复制代码
dojo.declare('infoq.demo.SomeDijit', [dijit._Widget], {
//dijit code
});

然后在页面中以声明方式创建实例:

复制代码
<div dojoType="infoq.demo.SomeDijit" sampleProp="sample"></div>

那么在 Dijit 中的代码就可以用 this.domNode 来引用到这个节点。而不再需要通过 dojo.byId 的方式来获取 DOM 节点的引用。

(2)使用模板创建 Dijit

顾名思义,模板定义了一个 Dijit 的骨架,假设有如下 Dijit 定义:

复制代码
dojo.declare('infoq.demo.TemplatedDijit', [dijit._Widget, dijit._Templated], {
templateString: dojo.cache('infoq.demo', 'templates/templated_dijit.html')
intro: 'demo template introduction'
//dojo.cache 用于直接获得指定文件内容,会在 build 时自动嵌入代码
//templateString 定义了一个 dijit 的模板内容
//other code
});

这就创建了一个基于模板的 Dijit,模板文件位于 script/infoq/demo/templates/templated_dijit.html(前面我们已经注册 script/infoq 对应于 infoq 命名空间)。假设模板文件内容如下:

复制代码
<div>
<img src='xx.png' dojoAttachPoint='imgAvatar'/>
<p dojoAttachPoint='introductionNode'>${intro}</p>
</div>

那么在页面上的 Dijit 就会以这个模板为骨架。在 dijit 代码中,可以使用 this.imgAvatar 来使用 img 节点。这里的 dojoAttachPoint 就是模板 dijit 中的特有属性,用于获取模板中包含的 DOM 节点或者 Dijit 的引用。例子里模板文件中的 ${intro}则会直接被替换为实例对象的 intro 属性。

测试及 Build 工具

测试及 Build 是完整开发的重要组成部分,如果大家感兴趣,在后续系列中将详细介绍这两块内容,这里作一个简单的介绍:

(1)测试工具:D.O.H

DOH 全称是 Dojo Objective Harness,是一个基于 Dojo 的单元测试工具,具有如下特点:

  • 既能够测试基于 Dojo 框架的 Ajax 应用,也能够测试基于非 Dojo 框架的 Ajax 应用(老版本只支持 Dojo 框架的)。最新版本现在是 0.9(独立于 Dojo 版本)。而且是 08 年就发布的,可见其还是相当稳定和够用的。
  • 能够将多个单元测试用例(多个 javascript,html)文件,在同一个界面上集中展示测试结果。
  • 既可以在浏览器中运行测试,也可以用命令行的方式来运行测试,命令行方式仅限于纯 javascript 代码的测试。
  • 提供基于浏览器的 Robot 插件,用于模拟鼠标键盘操作,方便界面测试。

大家可以通过 http://yourhost/dojoroot/util/doh/runner.htm 来运行 Dojo 自身的单元测试,体验其强大的功能。下面是其运行结果截图:

(2)Build 工具:Shrinksafe

ShrinkSafe 同样独立于 Dojo,但针对 Dojo 特别优化,用于打包和压缩 Ajax 应用。其主要提供了如下功能:

  • 自动处理源文件的依赖关系,打包多个文件到一个文件
  • 压缩 javascript 代码,去除注释,无用空白等
  • 处理及打包 i18n 文件
  • 自动嵌入 dojo.cache 引用的文件(如 dijit 的模板文件)

通常经过 build 过的代码,体积会减少 30%左右,并且因为将多个文件打包成一个,大大加快了浏览器加载速度,让交付的应用程序运行的更快。

这就是 Dojo

看完上文,相信大家对 Dojo 有了一个大概或者更深入的了解。一直以为 Dojo 是一款强大的工具,熟练掌握能让你的开发事半功倍。而 Dojo 的难用让它的用户相对较少,尤其是在国内。本文的目的正是希望通过对 Dojo 的系统性介绍,消除大家对 Dojo 的偏见,或者帮助那些想用 Dojo 的人用好 Dojo。

如果大家有任何问题或建议,或者希望看到哪些具体的 Dojo 功能介绍,欢迎留下评论,会在后面的文章中针对热点问题进行详细介绍。


感谢张凯峰的策划以及审校。

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

2010 年 12 月 09 日 00:0017176

评论

发布
暂无评论
发现更多内容

oeasy教您玩转linux 010216 随机诗词 fortunezh

o

如何进步神速

Sean

学习 个人成长

Python基础知识(二)

Python基础

配置时间特性

小知识点

大数据 flink scal

架构师训练营 - 大作业(二)

张明森

血的教训!千万别在生产使用这些 redis 指令

云流

redis 学习 编程 程序员

关于手机里的IP地址,你不得不知道的“秘密”

脑极体

windows平台python3使用impyla连接hive问题汇总

誓约·追光者

hive python3.x Windows 10

第一周学习总结

Geek_Albert

食堂就餐卡系统设计

Geek_Albert

食堂就餐卡系统设计

物流系统架构设计文档

莫莫大人

极客大学架构师训练营

Spring 5 中文解析数据存储篇-理解Spring事物抽象

青年IT男

Spring5 数据存储

宁静的可贵

谷鱼

宁静

LeetCode题解:622. 设计循环队列,使用数组,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

快三十岁了,网上玩赌博输掉了四百万后的忏悔

南昌体检代检

网上赌博输了怎么办 赌博玩快三输了怎么回血

【高并发】面试官:Java中提供了synchronized,为什么还要提供Lock呢?

冰河

Java synchronized 同步 lock 锁机制

甲方日常 16

句子

随笔杂谈

架构师训练营结业作业

superman

为什么很多人不买iPhone?

北柯

JDK15真的来了,一起来看看它的新特性

程序那些事

Java JDK15 JDK15新特性 java15新特性

升级Php Curl扩展遇到的坑

心平气和

php curl php扩展

架构师训练营大作业一

子豪sirius

直播倒计时|30分钟带你解锁“技术写作”新技能

郁敏

技术 写作 直播 技术创作 RTC征文大赛

Electronjs

Neil

Java Electron 前端框架 前端教程 客户端开发

字节高级工程师告诉我,想越过开发5年的“分水岭”这样做最适合

周老师

Java 编程 程序员 架构 面试

架构师训练营大作业

叮叮董董

网上赌博输了怎么办?上岸戒赌是唯一的选择

南昌体检代检

网上赌博输了怎么办 网上赌博玩快三输了怎办 网上玩快三输了怎么回血 网赌输了怎么戒赌

IP网络

菜鸟小sailor 🐕

python——dict常用方法

菜鸟小sailor 🐕

【高并发】面试官:说说缓存最关心的问题?有哪些类型?回收策略和算法?

冰河

缓存 面试 引用 offer 回收

关于java使用JDBC连接数据库

谷鱼

Java JDBC

演讲经验交流会|ArchSummit 上海站

演讲经验交流会|ArchSummit 上海站

Dojo:不容忽视的RIA框架-InfoQ