最近在做一个公司的项目,我们公司的角色是前端制作(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的查询模式,dojo为dojo.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'),或者dojo的dojo.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还是得费一番心思才行。
相关推荐
【Dojo 1.7 知识点详解】 Dojo 1.7 是 Dojo Toolkit 重要的版本更新,其中引入了显著的改进和新特性,尤其是对模块加载机制的革新。这一版本的主要焦点在于Asynchronous Module Definition (AMD),这是一种优化...
《Dojo 1.7 API CHM:深入理解与应用》 Dojo 是一个功能强大的JavaScript工具库,尤其在Web开发领域,它提供了一系列高级功能,包括模块化、数据管理、用户界面构建以及Ajax交互等。Dojo 1.7 API CHM文件是针对这个...
【Dojo 1.7 中文版本注释功能说明】 Dojo 1.7 是一个重大的版本更新,其中引入了显著的改进和新特性,尤其是对模块加载机制的革新。这一版本的核心亮点是实现了Asynchronous Module Definition (AMD)机制,这是一项...
7. **dojo/on**: Dojo 1.7 中的事件处理模块`dojo/on`提供了一种统一的方式来绑定和处理DOM事件,它兼容各种浏览器,提升了代码的可移植性和可维护性。 8. **dojo/aspect**: 这是一个面向切面编程(AOP)的模块,...
Dojo 1.7 版本是其历史上的一个重要里程碑,引入了许多改进和优化,包括模块化系统AMD(Asynchronous Module Definition)以及对Dijit UI 框架的增强。 首先,我们来详细了解一下Dojo 的核心部分。Dojo 框架的核心...
《dojo权威入门教程》是一本专注于JavaScript库Dojo Toolkit的学习指南,主要面向Java开发者或对Web前端技术感兴趣的人员。Dojo Toolkit是一个强大的JavaScript框架,它提供了丰富的组件、工具和API,帮助开发者高效...
dojo入门系列教程,包含入门简介,在javascript基础上介绍dojo的语法特色,ajax的dojo包装---xhr框架的编程要点, Dojo 事件机制.以及对dojo最具特色的web UI设计的全面介绍.
《Dojo入门》这本书主要介绍了Dojo JavaScript库的使用,帮助初学者快速掌握这个强大的前端开发工具。Dojo是一个开源的JavaScript框架,旨在提供一站式的解决方案,包括UI组件、数据管理、动画效果、Ajax交互等功能...
标题提到的"dojo1.8.chm"是Dojo 1.8版本的离线帮助文档,通常包含API参考、教程和示例等内容,方便开发者查阅。而"dojo1.11中文入门手册pdf"则是针对Dojo 1.11版本的中文学习资料,对于初学者来说是一份很好的学习...
《dojo china extjs 视频教程源码》 在当今的Web开发领域,JavaScript框架扮演着至关重要的角色,其中Dojo和ExtJS是备受开发者喜爱的两个库。本教程源码集结合了这两个强大的工具,为学习者提供了一次深入理解它们...
精通Dojo(中文版),之前自己学习的时候看的,质量挺高,百度网盘地址
《Dojo 1.7 深入解析与应用实践》 Dojo 是一个全面的JavaScript工具库,专为构建富互联网应用程序(RIA)而设计。这个“dojo-release-1.7.1-src”压缩包包含了Dojo 1.7.1的源代码,为开发者提供了深入了解和自定义...
手册中列举了一些常用的Dojo包,如dojo.io用于不同类型的IO传输,dojo.dnd提供拖放功能的API,dojo.string提供了字符串处理方法,dojo.date帮助解析和操作日期,dojo.event处理事件驱动和AOP开发,dojo.back管理撤销...
"dojo-release-1.7.3"是Dojo框架的一个特定版本,发布于2012年,这个版本在1.7系列中被认为是稳定且广泛使用的。 1. **模块化系统(AMD)** Dojo 1.7 引入了Asynchronous Module Definition(AMD),这是一种异步...
dojo精品中文教程 Dojo.1.0 Practice Note [1] 什么是dojo 选择dojo的理由 AJAX架构之Dojo篇 Adding Ajax中文版 (DoJo) DOJO学习笔记(七)-日期控件DropdownDatePicker和DatePicker DOJO常用的验证函数 ...
在入门Dojo时,首先需要了解如何安装和引入Dojo到项目中。这通常涉及到在HTML文件中添加Dojo的CDN链接或者下载Dojo库并本地引用。同时,设置Dojo的配置选项,如dojoConfig,用于定义模块加载的路径、调试模式等。 ...
这个"dojo 官方教程 中文翻译"是针对Dojo框架的全面指南,帮助中文用户更好地理解和应用Dojo。 在Dojo官方教程中,你可以学习到以下核心知识点: 1. **Dojo基础**:了解Dojo的核心概念,如dojo/_base模块,这是...
### Dojo 快速入门知识点详解 #### 一、Dojo 概览 Dojo 是一个功能强大且灵活的开源 JavaScript 库,主要用于构建高性能的富客户端 Web 应用程序。Dojo 提供了一系列工具和组件,使得开发者能够轻松创建交互式 Web...