`

dojo 1.7简易入门教程(转)

    博客分类:
  • Dojo
 
阅读更多

最近在做一个公司的项目,我们公司的角色是前端制作(HTML+CSS+JS),开发方为IBM,所以前端的Javascript被指定了使用dojo。本来是没有我什么事情,可是偏偏眼睛里不容沙子,非得要求重构,于是开始漫漫的HTML重构和使用dojo的过程。初初上手dojo,真的觉得十分苦恼,因为介绍它的文档实在少之又少,尤其是1.7版本。偏偏1.7版本又是dojo一个较为重要的升级版本——引入了AMD的机制,所以想要把代码写得更加dojo些,还着实花了我不少的精力去看这个新版本的源代码,包括dijit和dojox部分。

基于dojo 1.7的js目录规划

为什么要说这点呢,因为实现了AMD机制的,如require.js的,对目录的规划尤为重要,因为涉及到require加载的模式,而dojo 1.7尤甚。

基本上,dojo1.7已经默认会认为你的目录是基于如下的规划的:

/webroot/js/dojo/
/webroot/js/dijit/
/webroot/js/dojox/

则,当一个/webroot/index.html的页面,尝试按照上述目录加载的时候:

<script type="text/javascript" src="js/dojo/dojo.js"></script>

它会默认认为,js的目录,为所有modules的基础目录,即当你希望添加一个叫做app的新module时:

define('app', ['dojo/_base/kernel'], function(dojo) {
	return {
		anyMethod: function() {
			// ...
		}
	}
});

require(['app'], function(app) {
	app.anyMethod();
});

而该文件则放在/webroot/js/app.js目录下即可。

当然,dojo本身提供多种声明dojoConfig的模式,但是根据我的尝试,会认为上述的实践模式会令项目较为简单且易于他人理解。dojo提供了baseUrl用于声明require的基础路径入口,我尝试了,由于dojo本身的目录层级问题,会存在一些小问题,类如:

baseUrl: 'js/modules' // 尝试告诉dojo,所有项目自定义的modules的目录

而由于dojo的目录层级关系(js/dojo,为该目录的上一级),他会同样认为你声明的这个目录也是在js/module的上一级,即还是指向了js这个目录。当然,我也可以将baseUrl改为js/modules/any,来强迫使他指向js/modules,但这实在不雅。

当然,实在不行,你还是可以通过声明paths、packages等模式来强迫指定某个基础的paht、package的入口,但是总归来说,还是让理解项目结构、代码等造成更大的困扰性,都就此作罢。既然dojo本就如此,就按照回他本身默认的机制、设定去规划目录,将会是走最少弯路的实践模式。

dojoConfig声明模式和重要参数说明

dojoConfig声明模式支持3种:

<script type="text/javascript">
var dojoConfig = {
	async: false
	// more ...
}
</script>
<script type="text/javascript" src="js/dojo/dojo.js"></script>

 

<script type="text/javascript" src="js/dojo/dojo.js" data-dojo-config="async: false, parseOnLoad: false, isDebug: true"></script>

 

<script type="text/javascript" src="js/dojo/dojo.js"></script>
<script type="text/javascript">
require({
	async: false,
	parseOnLoad: false
});
</script>

实践证明,第二种模式会是比较易于理解,且又不会在页面写太多代码的模式。

而dojoConfig中,比较重要的参数即为async和parseOnLoad(就我目前的使用程度来说),简单介绍如下:

async: true | false | legacyAsync

async表示的是dojo core(其实就是dojo/dojo.js所定义的模块、方法)是否使用异步的模式加载,而其中,我至今未试验出legacyAsync的模式为何,而true | false主要差异为:

async: true,启用异步模式,则不存在全局对象dojo,而且你即使:

require(['dojo/_base/kernel'], function(dojo) {
	console.log(dojo);
});

你会发现dojo的实例里面,只包含了最精简的一些方法和api,你需要逐个逐个你需要使用的module去将其加载进去。

而使用了false,则dojo/dojo.js中声明定义了的API,他都会以全局的dojo实例装载之。

使用true和false,分别有其用途,尤其在使用了true,我认为,是可以同时使用多个不同版本的dojo实例分别使用的(只是显得有些叶公好龙、画蛇添足)。

而parseOnLoad,则是指dojo在页面自动加载时,会使用dojo/parser库自动解析页面指定了data-dojo-*的DOM标签,比如<select data-dojo-type="dijit.form.ComboBox">,自动转化为dijit.form.ComboBox的widget。不过话说,如果你启用了parseOnLoad: true,碰到页面有这种data-dojo-type,而你又没有事先require了相关的类库,他是会抛出一个js error的。而且说句实话,光是一个dijit.form.ComboBox,他就会require一大堆js文件,真是为这个页面感到费劲啊。

DOM基础使用习惯

据我所知,dojo曾在某一段时间内和MooTools合作过,dijit就是他们合作后的产物之一,而包括像David Walsh等,这些MooTools开发团队成员,也在自己的博客大力鼓吹Dojo(至今),为其拉广告、做宣传,所以可以想见dojo受MooTools的影响之深。

而事实上,dojo的DOM方法中,存在和MooTools极其接近的定义结构,他同时支持两种DOM查询模式:

1、传统的byId的模式,dojo采用的是dojo.byId('elId'),而MooTools则是document.id('elId')。

2、xpath的查询模式,dojodojo.query('#id'),而MooTools则是,document.getElements('#id')。

为何说dojo更加类MooTools而不是说类jQuery呢(同样支持xpath的query),基于以下几点:

1、jQuery的概念里面,是不存在支持byId的查询模式的。

2、 jQuery的操作模式里面,是不支持批量操作的,即,jQuery('.submit-btn').css(),表示的是对该query的第一个element进行操作  而MooTools和dojo的操作是批量的操作,即:document.getElements('.submit-btn').addClass('focus'),或者dojodojo.query('.submit-btn').style({ color: 'red' })

3、而且从定义对象结构上来说,dojo更像MooTools,即:
dojo:
dojo.byId() -> HTML Element实例
dojo.query() -> dojo.NodeList类实例
MooTools:
document.id() -> Element类
document.getElements() -> Elements类实例

4、链式操作更类似MooTools,即:
dojo:
dojo.query('.any-class').query('anySubElement').doSomeThing();
MooTools:
document.getElements('.any-class').getElements('anySubElement').doSomeThing();

所以在使用dojo的DOM方法的时候,最好养成一个统一的使用习惯,不然一会儿dojo.byId,一会儿dojo.query,非让日后跟进代码的人看得头冒青烟不可。

在这里,我推荐使用dojo.NodeList的模式作为基础操作,原因基于如下:

1、dojo.NodeList更易于使用链式操作模式,即:dojo.query('.any-class').attr({  }),但是千万注意,这里会对所有query到得element做同样的attr操作。

2、dojo为了控制性能和不污染原生DOM对象,不采用原型链的方式去扩展DOM Element,于是操作一个原生的DOM Element在dojo里的做法显有些蹩脚,如:dojo.attr(DOMElement, {}),dojo.style(DOMElement, {}),使用起来真的不太舒服。

于是有人会问,假定我已经获得了某个DOMElement,那如何能快速的进行操作呢?以下举个例子:

 

// $ 伪装成大家习惯的jQuery操作符
require(['dojo/_base/kernel', 'dojo/dom', 'dojo/query'], function(dojo, dom, $) {
	var el = dom.byId('el_id');
	// 正统的操作模式应该是:
	dojo.style(el, { display: 'none' });
	// 不过事实上,你可以这样写,会让你的代码写得舒服,别人读起来也更加舒服
	$(el).style({ display: 'none' });
	// 这个例子是可以延伸发展下去的:
	$($('body')[0]).style({ display: 'none' }); // 这是类jQuery的查询模式
	$($('#any_id')[0]).style({ height: 100 + 'px' });
});

 

 

如果$('#any_id')[0],不存在,则$($('#any_id')[0]),的NodeList内容是空的,则其后的style操作也不会继续进行,这可以缓解很多时候,为了判断某个element是否存在,而写了大堆if ... else的判断结构。

dojo的DOMEvent

dojo的DOM事件,潜伏得比较深,不过为了深入简出,这里只谈两个问题:

1、怎么快速绑定事件?

2、怎么stop一个事件。

基于上述的NodeList的操作习惯,有以下的代码:

 

require(['dojo/_base/kernel', 'dojo/query'], function(dojo, dom, $) {
	$('body').connect('mousemove', function(event) {
		
	});
	$('tag').on('click', function(event) {
		
	});
});

 

connect是dojo一个比较特殊的方法,他用于实现了对任意对象,实现事件注入(类似Qt框架的信号槽的机制),很多对象,类如dojo.Animate的事件绑定,也是经过此方法绑定。

 

stopEvent的方式:

 

require(['dojo/_base/kernel', 'dojo/query', 'dojo/_base/event'], function(dojo, $) {
	$('body').connect('mousemove', function(event) {
		dojo.stopEvent(event); // 是不是有点好像以前写JS原生的stop event呢?
	});
});

 

和jQuery不同,jQuery的事件只要return false即可,dojo需要额外引入 dojo/_base/event并执行dojo.stopEvent(event),这让我想很早以前兼容IE和Firefox时候的写法,event = event || window.event。

 

Animate的使用

dojo的Animate,顶多只实现到了mootools的tween的级,还没到Fx.Morph。他实现代码上,当你使用dojo.animateProperty创建一个新的animate实例时,他会不断的创建新的animate实例,如果大量的这样写,你会发现你写的JS效果,大量存在:非同步,抖动厉害的情况出现。不过这个问题,也非一时半刻能说得明白的问题,对于简单的特效,还是可以使用dojo.animateProperty的,但是对于稍微复杂一些的效果的时候,则需要注意用法,这里简单说明一下:

 如上图显示,眼下要做一个Slider,里面除了有两个toPrev、toNext两个操作按钮外,中间有一个存放图片的容器,这个容器用于展现该容器内的图片的滚动。

假定我们限定了SliderWrap的宽度,比如370px,使其永远只会显示两张图片,要显示更多的图片,我们将对SliderWrap使用margin-left,使其margin-left为-xxxpx来实现其slider的滚动。

一个基本的做法就是:

 

$('#toPrev').connect('click', function() {
	dojo.animateProperty({
		node: $('#SliderWrap')[0],
		properties: {
			marginLeft: anyPx
		}
	}).play(;
});

$('#toNext').connect('click', function() {
	dojo.animateProperty({
		node: $('#SliderWrap')[0],
		properties: {
			marginLeft: anyPx
		}
	}).play();
});

 

但是,一个预料之外的事情的发生了,用户狂点toNext的按钮,这时候,你的slider滚动的就没有那么流畅了,而是会出现卡顿、不流畅、不自然等情况发生(因为多次click的事件被执行了)。使用click作为触发的event还是比较好处理的,因为他必须满足click的条件——mousedown & mouseup,如果你将触发的事件设置为mouseenter(mouseover),恐怕情况会急剧的恶化下去。

如果用MooTools来实现同样的效果,会考虑改为如下:

 

var fx = new Fx.Morph($('SliderWrap'));

$('toPrev').addEvent('click', function() {
	fx.pause().start({ 'margin-left': anyPx });
});

$('toNext').addEvent('click', function() {
	fx.pause().start({ 'margin-left': anyPx });
});

 

差异主要在于pause(),假定用户以很高频率点击toNext,那么不管之前fx播放到什么情况,都先暂停执行,而后将fx播放至用户当前的请求操作下。

 

而同样的,在dojo,则需要这样做:

 

var anim = dojo.animateProperty({
	node: $('#SliderWrap')[0]
});

$('#toPrev').connect('click', function() {
	anim.properties = {
		marginLeft: anyPx
	};
	anim.stop().play();
});

$('#toNext').connect('click', function() {
	anim.properties = {
		marginLeft: anyPx
	};
	anim.stop().play();
});

 

附录1:常用方法整合

至此,dojo使用上的一些小问题,已经基本解决,不过为了辅助dojo的编程,我还是写了几个小方法,为了改善dojo的编程代码过于冗余,过于繁重的情况,附录如下:

 

 

(function(app) {

	/**
	 * 获取item的类型,即typeof item === ?
	 *
	 * 取自MooTools 1.4
	 */
	var typeOf = this.typeOf = function(item) {
		if (item == null) return 'null';
		if (item.nodeName){
			if (item.nodeType == 1) return 'element';
			if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
		} else if (typeof item.length == 'number'){
			if (item.callee) return 'arguments';
		}
		return typeof item;
	};

	/**
	 * 判断某个item是否为某个对象的实例
	 *
	 * 取自MooTools 1.4
	 */
	var instanceOf = this.instanceOf = function(item, object) {
		if (item == null) return false;
		var constructor = item.$constructor || item.constructor;
		while (constructor){
			if (constructor === object) return true;
			constructor = constructor.parent;
		}
		/*<ltIE8>*/
		if (!item.hasOwnProperty) return false;
		/*</ltIE8>*/
		return item instanceof object;
	};

	define('app', ['dojo/_base/kernel', 'dojo/_base/lang', 'dojo/dom', 'dojo/query', 'dojo/_base/NodeList', 'dojo/_base/event'], function(dojo, lang) {

		/**
		 * 传入DOM Element | dojo.NodeList | string,取出指定index的DOM Element
		 * 为了在某些场景需要明确的操作某个DOM Element使用
		 *
		 * @param DOMElement|dojo.NodeList|string el
		 * @param int index
		 * @return DOMElement|false
		 */
		var element = app.element = function(el, index) {
			if (!index)
				index = 0;
			if (typeOf(el) == 'string') {
				var elById = dojo.byId(el);
				if (el.match(/\#|\.|\s/, el) || !elById)
					el = dojo.query(el);
				else
					el = elById;
			}
			if (typeOf(el) == 'object' && instanceOf(el, dojo.NodeList))
				el = el[index];
			return typeOf(el) == 'element' ? el : false;
		};

		/**
		 * 执行animate,并返回该animate对象
		 *
		 * @param DOMElement|dojo.NodeList|string el
		 * @param object styles 需要animate执行的样式
		 * @param object args   其他animate参数
		 * @return dojo.Animate
		 */
		var animate = app.animate = function(el, styles, args) {
			if (element(el) !== false) {
				if (typeOf(args) === 'function') {
					args = { onEnd: args };
				}
				else {
					args = args || {};
				}
				args.node = element(el);
				args.properties = styles;
				return dojo.animateProperty(args).play();
			}
			return false;
		};

		/**
		 * 生成animate对象,但不play
		 *
		 * @param DOMElement|dojo.NodeList|string el
		 * @param object styles 需要animate执行的样式
		 * @param object args   其他animate参数
		 * @return dojo.Animate
		 */
		var animateObject = app.animateObject = function(el, styles, args) {
			if (element(el) !== false) {
				if (typeOf(args) === 'function') {
					args = { onEnd: args };
				}
				else {
					args = args || {};
				}
				args.node = element(el);
				args.properties = styles;
				return dojo.animateProperty(args);
			}
			return false;
		};

		/**
		 * 当某个DOMEvent发生时,判断当前DOMEvent是否经过某个DOMElement
		 *
		 * @param DOMElement|dojo.NodeList|string el
		 * @param DOMEvent event
		 * @return bool
		 */
		var hovered = app.hovered = function(el, event) {
			if (element(el) === false)
				return false;
			var coords = dojo.coords(element(el));
			var x = event.pageX, y = event.pageY;
			return (x >= coords.x && x <= coords.x + coords.w && y >= coords.y && y <= coords.y + coords.h)
		};

		return app;
	});

}).call(this, this.app || {});

 

整体而言,dojo还是一个很不错的框架,只是文档少得有些可怜,而且大多比较老,想用好dojo还是得费一番心思才行。
分享到:
评论
1 楼 vvvpig 2012-08-26  
其实主要是变化太快,1.7的时候就开始叫嚣2.x快出了然后文档中已经出现一堆不建议使用的API,有的还说道2.x的时候会删除,API显得特别不稳定,关于Ajax的工具代码先后出了五六个版本,到1.8的时候又加了一个版本,文档根本不可能很清楚,就像现在的Node,版本变化太快,早期根本没法写文档,等文档出来,早就过时了

相关推荐

    Dojo 1.7 版本注释.docx

    【Dojo 1.7 知识点详解】 Dojo 1.7 是 Dojo Toolkit 重要的版本更新,其中引入了显著的改进和新特性,尤其是对模块加载机制的革新。这一版本的主要焦点在于Asynchronous Module Definition (AMD),这是一种优化...

    Dojo1.7 Api chm

    《Dojo 1.7 API CHM:深入理解与应用》 Dojo 是一个功能强大的JavaScript工具库,尤其在Web开发领域,它提供了一系列高级功能,包括模块化、数据管理、用户界面构建以及Ajax交互等。Dojo 1.7 API CHM文件是针对这个...

    Dojo 1.7 中文版本注释功能说明

    【Dojo 1.7 中文版本注释功能说明】 Dojo 1.7 是一个重大的版本更新,其中引入了显著的改进和新特性,尤其是对模块加载机制的革新。这一版本的核心亮点是实现了Asynchronous Module Definition (AMD)机制,这是一项...

    dojo 1.7 最新dojo包,内含最新的实例若干个。

    7. **dojo/on**: Dojo 1.7 中的事件处理模块`dojo/on`提供了一种统一的方式来绑定和处理DOM事件,它兼容各种浏览器,提升了代码的可移植性和可维护性。 8. **dojo/aspect**: 这是一个面向切面编程(AOP)的模块,...

    dojo 源码1.7汇总

    Dojo 1.7 版本是其历史上的一个重要里程碑,引入了许多改进和优化,包括模块化系统AMD(Asynchronous Module Definition)以及对Dijit UI 框架的增强。 首先,我们来详细了解一下Dojo 的核心部分。Dojo 框架的核心...

    dojo权威入门教程

    《dojo权威入门教程》是一本专注于JavaScript库Dojo Toolkit的学习指南,主要面向Java开发者或对Web前端技术感兴趣的人员。Dojo Toolkit是一个强大的JavaScript框架,它提供了丰富的组件、工具和API,帮助开发者高效...

    dojo入门系列教程.rar

    dojo入门系列教程,包含入门简介,在javascript基础上介绍dojo的语法特色,ajax的dojo包装---xhr框架的编程要点, Dojo 事件机制.以及对dojo最具特色的web UI设计的全面介绍.

    图书:Dojo入门

    《Dojo入门》这本书主要介绍了Dojo JavaScript库的使用,帮助初学者快速掌握这个强大的前端开发工具。Dojo是一个开源的JavaScript框架,旨在提供一站式的解决方案,包括UI组件、数据管理、动画效果、Ajax交互等功能...

    dojo1.8.chm+dojo1.11中文入门pdf

    标题提到的"dojo1.8.chm"是Dojo 1.8版本的离线帮助文档,通常包含API参考、教程和示例等内容,方便开发者查阅。而"dojo1.11中文入门手册pdf"则是针对Dojo 1.11版本的中文学习资料,对于初学者来说是一份很好的学习...

    Dojo快速入门教程

    ### Dojo快速入门教程 Dojo 是一个功能强大的面向对象 JavaScript 框架,它能够帮助开发者构建高性能、跨平台的 Web 应用程序。本教程将详细介绍 Dojo 的核心概念、基本用法以及如何利用 Dojo 开发 Web 应用。 ###...

    dojo china extjs 视频教程源码

    《dojo china extjs 视频教程源码》 在当今的Web开发领域,JavaScript框架扮演着至关重要的角色,其中Dojo和ExtJS是备受开发者喜爱的两个库。本教程源码集结合了这两个强大的工具,为学习者提供了一次深入理解它们...

    dojo教程 dojo 教程大全

    精通Dojo(中文版),之前自己学习的时候看的,质量挺高,百度网盘地址

    dojo-release-1.7.1-src

    《Dojo 1.7 深入解析与应用实践》 Dojo 是一个全面的JavaScript工具库,专为构建富互联网应用程序(RIA)而设计。这个“dojo-release-1.7.1-src”压缩包包含了Dojo 1.7.1的源代码,为开发者提供了深入了解和自定义...

    DOJO API 中文参考手册,附加注解实例(精心重新排版DOC文档)

    手册中列举了一些常用的Dojo包,如dojo.io用于不同类型的IO传输,dojo.dnd提供拖放功能的API,dojo.string提供了字符串处理方法,dojo.date帮助解析和操作日期,dojo.event处理事件驱动和AOP开发,dojo.back管理撤销...

    dojo-release-1.7.3

    "dojo-release-1.7.3"是Dojo框架的一个特定版本,发布于2012年,这个版本在1.7系列中被认为是稳定且广泛使用的。 1. **模块化系统(AMD)** Dojo 1.7 引入了Asynchronous Module Definition(AMD),这是一种异步...

    dojo精品中文教程(全)

    dojo精品中文教程 Dojo.1.0 Practice Note [1] 什么是dojo 选择dojo的理由 AJAX架构之Dojo篇 Adding Ajax中文版 (DoJo) DOJO学习笔记(七)-日期控件DropdownDatePicker和DatePicker DOJO常用的验证函数 ...

    Dojo 入门 + Dojo 工具包系列 + Dojo 使用技巧 ......

    在入门Dojo时,首先需要了解如何安装和引入Dojo到项目中。这通常涉及到在HTML文件中添加Dojo的CDN链接或者下载Dojo库并本地引用。同时,设置Dojo的配置选项,如dojoConfig,用于定义模块加载的路径、调试模式等。 ...

    dojo 官方教程 中文翻译

    这个"dojo 官方教程 中文翻译"是针对Dojo框架的全面指南,帮助中文用户更好地理解和应用Dojo。 在Dojo官方教程中,你可以学习到以下核心知识点: 1. **Dojo基础**:了解Dojo的核心概念,如dojo/_base模块,这是...

    dojo快速入门文档

    ### Dojo 快速入门知识点详解 #### 一、Dojo 概览 Dojo 是一个功能强大且灵活的开源 JavaScript 库,主要用于构建高性能的富客户端 Web 应用程序。Dojo 提供了一系列工具和组件,使得开发者能够轻松创建交互式 Web...

Global site tag (gtag.js) - Google Analytics