DOJO试用手记 - Enjoy Lucky Life Eternally - CSDNB...

来源:百度文库 编辑:神马文学网 时间:2024/03/29 19:12:16
1、前言
最近发现ajax很不错,由于它的存在,b/s结构的应用,在使用上与c/s结构的更接近了。至于什么是ajax,这就不是我所要说明的,不清楚的自己去网上搜索。
由于看ajax的东西,进而找到几个与此相关的框架。由于我是做java的,一般用eclipse,eclipse的编辑javascript的插件 JSEclipse中,有对dojo的支持,而我以前看的eclipse中对ajax的支持框架中也有dojo,所以,我就研究dojo了。
首先,当然是把整个框架下载下来,在http://dojotoolkit.org/download/里就可以下载到最近的dojo了。下完后,看了下demo,就开始满互连网的搜索关于dojo的东西,发现还真是不多,而且不是讲得很清楚,于是,就有想写点东西了。
下完后,将dojo.js和src里的东西都拷出来,就开始了dojo之旅。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2、Event System
首先接触到的,是dojo的Event System。
以前,我们是由页面控件触发一系列时间的时候,整个事件链要先定义好,然后才能按需要调用已经写好的调用模块,一旦要修改事件链,就不是那么容易了。由 一个函数调用另一个函数,能随便说要调用哪个吗?能在运行的时候很容易修改吗?除了那些大师级的人物,我相信我们这些菜鸟要解决这些问题,要费不少事。
在dojo中,其关注的事件不仅仅是Dom事件,它把任何的js方法调用都看作可以侦听的事件,这就把一切都统一到一个地方了。
我们都写过这样的代码:
1 var buttonNode= document.getElementById("button");
2 function foo()
3 {
4     alert("foo");
5 }
6 buttonNode.onclick = function()
7 {
8     foo();
9 }
要调用foo函数,需要这么写,如果说接着我还要调用一个函数呢?那么,就需要重写buttonNode的onclick事件函数,把以前的都再写一遍,如果我还要再调用呢。。。。
我们来看看dojo中是怎么解决的,看下面的一句
1 dojo.event.connect(buttonNode,"onclick","foo");
就这么一句,就绑定了触发函数,想再加?那就继续用dojo.event.connect(buttonNode,"onclick","foo2")...
还有这么一种写法:
dojo.event.connect(handlerNode, "onclick", function(evt){
// 
});
上面是buttonNode绑定一个函数,如果要与某对象的某个函数绑定的话,就用
dojo.event.connect(buttonNode, "onclick", object, "handler");
object是目标对象,handler是目标对象的函数,这里要注意,object不仅仅是页面控件,一切对象皆可行,就又回到“关注的事 件不仅仅是Dom事件,它把任何的js方法调用都看作可以侦听的事件”。要解除绑定的话,就可以使用dojo的disconnect方法,调用参数一定要 与connect一致,即可解除之前的绑定操作。
dojo中connect函数的参数有下面几种: object, name, name
object, name, function pointer
object, name, object, name
再看看这段:
1 var exampleObj = {
2     counter: 0,
3     foo: function(){
4         alert("foo");
5         this.counter++;
6     },
7     bar: function(){
8         alert("bar");
9         this.counter++;
10     }
11 };
12
13 dojo.event.connect(exampleObj, "foo", exampleObj, "bar");
14
15
最后一句的作用是什么?使得执行exampleObj的foo函数之后,执行exampleObj的bar函数,一切对象皆可绑定!
为了防止不经意间对事件的多处绑定,造成连锁调用。Dojo提供关键字链绑定,比如可以只绑定一次:
1 dojo.event.kwConnect({
2   srcObj: exampleObj,
3   srcFunc: "foo",
4   targetObj: exampleObj,
5   targetFunc: "bar",
6   once: true
7 });
同样,对应也提供了一个kwDisconnect()方法来进行关键字绑定的解除。
在connect()和KwConnect()中,可以实现延迟执行和循环执行。
KwConnect()中,只需要加一个delay属性就可以了,测试代码如下:  1 

 New Document 





9
10 
11
12 
13 
14 
15
16 var exampleObj = {
17     counter: 0,
18     foo: function(){
19         alert("foo");
20         this.counter++;
21     },
22     bar: function(){
23         alert("bar");
24         this.counter++;
25     }
26 };
27
28 dojo.event.kwConnect({
29   srcObj: exampleObj,
30   srcFunc: "foo",
31   targetObj: exampleObj,
32   targetFunc: "bar",
33   delay: 3000
34 });
35 dojo.event.connect(document.getElementById("eee"),"onclick",exampleObj,"foo");
36
37
38 
39 
40 
由上面的延迟,可以想到,如果目标等于源的话,那么就是一个循环执行!
据说在connect()中,延迟信息在它的第九个参数,具体怎么样,我还没去试。
上面是在事件发生后调用目标,如果要在发生前呢?就是下面的东西了:
dojo.event.connect("before", exampleObj, "foo", exampleObj, "bar");
很容易理解吧,我就不多说了。在KwConnect中,就是
dojo.event.kwConnect({
type:       "before",
srcObj:     exampleObj,
srcFunc:    "foo",
targetObj:  exampleObj,
targetFunc: "bar"
});
默认情况下,connect()中第一个参数就是"after"了,同理KwConnect中的type默认是"after"。
下面要说的是方法包装。当我们想改变方法的输入输出时,一般情况下是直接去修改代码,那么,如果不修改原方法怎么办呢?在dojo中,也给出了解决方法,就是用Around advice包装方法。下面是一个例子:

 New Document 





function foo(arg1, arg2)
{
return arg1+arg2;
}
function aroundFoo(invocation){
if(invocation.args.length < 2){
invocation.args.push(3);
}
var result = invocation.proceed();
//result="sss";
return result;
}
dojo.event.connect("around", "foo", "aroundFoo");
dojo.event.connect(document.getElementById("eee"),"onclick",function(){alert(foo(1))});



结果是4,如果取消注释,则结果为"sss",开始调用的时候,只传了个参数1,经过包装处理,添加了一个默认参数,然后继续,函数执行完毕后,还可以将输出结果再处理一遍,当然,只是在函数有返回值的时候。
这里要注意的是:函数aroundFoo有且只能有一个参数,就是要改变的方法对象。这样,每次执行foo函数时,都会进行包装,然后再输出。
利用connect()的时候,有一个问题就是参数传递,参数不一致,该怎么办?先看下面一段:  1 

 New Document 



7 var obj1 = {
8     twoArgFunc: function(arg1, arg2){
9         alert("1:"+arg1+" "+arg2);
10     }
11 };
12
13 var obj2 = {
14     oneArgFunc: function(arg1,arg2){
15         alert("2:"+arg1+" "+arg2);
16     }
17 };
18
19 dojo.event.connect(obj1, "twoArgFunc",
20                     obj2, "oneArgFunc");
21
22 obj1.twoArgFunc(1,4);
23 
24 
25 
结果是怎样的呢?2个连接的函数的参数相同!所以,要传递参数到另外一个函数中,已经不需要我们多做什么,dojo已经传过去了,参数的格式不一致的话,我们只需要再包装一下目标函数。
网上的那个例子我怎么也调试不成功,花了点时间,改了下,终于好了,下面是代码:


 New Document 

5

7


10 var obj1 = {
11     twoArgFunc: function(arg1, arg2){
12         // 需要2个参数
13         alert("1: "+arg1+" "+arg2);
14     }
15 };
16
17 var obj2 = {
18     oneArgFunc: function(arg1){
19         //只需要一个数组作为参数
20         alert("2: "+arg1);
21     }
22 };
23
24 function aroundFunc(invocation){
25     var tmpArgs = [
26                     invocation.args[0],
27                     invocation.args[1]
28                   ];
29     invocation.args = [tmpArgs];
30     return invocation.proceed();
31 }
32
33 // after-around advice
34 dojo.event.connect("after",obj1, "twoArgFunc",obj2, "oneArgFunc","aroundFunc");
35
36 //也可以写成下面2句
37 //dojo.event.connect(obj1, "twoArgFunc",obj2, "oneArgFunc");
38 //dojo.event.connect("around",obj2,"oneArgFunc","aroundFunc");
39
40 obj1.twoArgFunc(1,4);
41 
42 
43 
44
要注意的是,34行的after不能少,少了就触发不了了,照道理默认就是after的啊,具体可能是dojo内部问题吧。
接下来介绍匿名通信。
对象之间,不可能总是互相可见的,可能要连接的对象不是同时产生的,也就是说,异步产生,这样的话,用connect()就不是那么方便了,什么时候 connect(),就是个问题了。dojo中,"Topics to the rescue!",dojo是利用topic机制来解决的。看下面的代码
1 var exampleObj = {
2     counter: 0,
3     foo: function(){
4         alert("foo");
5         this.counter++;
6     },
7     bar: function(){
8         alert("bar");
9         this.counter++;
10     }
11 };
12
13 // previously we used this connect syntax
14 //
15 //  dojo.event.connect(exampleObj, "foo", exampleObj, "bar");
16 //
17 // which we now replace with:
18
19 // set up our publisher
20 dojo.event.topic.registerPublisher("/example", exampleObj, "foo");
21
22 // and at some point later, register our listener
23 dojo.event.topic.subscribe("/example", exampleObj, "bar");
24
25
由上面可以看到,连接是分步进行的。在前面说明,要连接一个对象,具体是哪个,它可以不用知道,后面,可以指定一个连接对象,这样,2个连接的对象,不知道对方是谁,因为它们是通过发布/订阅机制通信,是通过中转的。这么做有什么好处?不用我说了吧。这就是匿名通信。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3、异步通信
既然dojo是ajax的一个框架,那异步通信是必不可少的,否则叫什么ajax。dojo中,这一部分是在dojo.io包中的。
先看看这一段:  1 // an asynchronous request to foo.php that returns a JavaScript literal
2 // which is eval()‘d
3 var bindArgs = {
4     url:        "foo.php",
5     mimetype:   "text/javascript",
6     error:      function(type, errObj){
7         // handle error here
8     },
9     load:      function(type, data, evt){
10         // handle successful response here
11     }
12 };
13
14 // dispatch the request
15 var requestObj = dojo.io.bind(bindArgs);
16
17
18 //当然,可以写成
19 //dojo.io.bind({
20 //    url:        "t4.htm",
21 //    mimetype:   "text/javascript",
22 //    error:      function(type, errObj){
23 //        // handle error here
24 //    },
25 //    load:      function(type, data, evt){
26 //        // handle successful response here
27 //    }});
28
dojo.io.bind(requestObject)方法中,只有一个参数,就是请求对象,请求对象可以是dojo.io.Request,也可以是指明了dojo.io.Request基本属性的一个匿名对象。在上面的代码中,我们没有new一个dojo.io.Request对象,所以使用的是匿名对象,在应用中,常用的是匿名对象。
由上面的匿名对象的代码,可以看到dojo.io.Request对象有2个属性和2个方法:
1."url",表示被请求的资源地址。
2."mimetype",回应内容类型,默认是"text/plain",注意与http传输时的mimetype区别,当mimetype为"text/javascript"时,表示请求javascript脚本并执行。
3."error"方法,表示请求失败时的处理。
4."load"方法,请求成功后的操作。
dojo.io.Request对象的sync属性默认为false,也就是说不同步(就是异步了),发出请求后,可继续后续操作,就是所谓的异步操作了。一旦该项取值为true,发出请求,则必须等待请求处理完毕,才能继续后续操作,否则将一直阻塞操作。
上面的代码没有指定sync属性,就是说其为默认值false,所以是异步执行的。
dojo.io.Request对象还有几个属性,介绍如下:
method:相信form用多的人都不会陌生,对了,就是和它一个意思,取值为"POST"或"GET",默认取值为"GET"。
formNode:一个DOM节点,指定一个form将被此次请求提交,其将继承上面的url和method属性,若前面没给method属性赋值,则取该form的method属性。该属性取值如document.getElementById("form1")。
content:键/值对,将被加在参数里被传输,相当于http传输方式中get和post的数据。
transport: 指明此次传输所用的传输对象,一旦指定的传输对象不可用,则此次请求将失败,同时触发error事件。若不指定,bind()方法将试图从已经注册的传输 对象列表中查找最合适的可用的传输对象来执行请求。如要确认用XMLHttp来执行请求,则需指定:transport: "XMLHTTPTransport"。下面是bind()试图查找最合适的传输对象的代码部分: 1 for(var x=0; x2 {
3     var tmp = dojo.io.transports[x];
4     if((this[tmp])&&(this[tmp].canHandlerequest)))
5     {
6         tsName = tmp;
7     }
8 }
changeUrl: 请求完成后,发出请求的页面是否将跳转到某锚点。虽然官方文档解释的时候说是boolean类型,默认为false,但是该项也可以是自定义的字符串,就 是要跳转到的锚点名称。当该项为true时,锚点名称将根据当前时间生成,具体有什么用处我也不清楚。但是我们可以通过指定锚点名称,来跳转到我们希望的 锚点。要注意的是,不管请求处理成功与否,都会发生跳转。官网文档中,说在Mozilla/Firefox中,URL改变将失败,也就有了这句"This may be removed in the future as it pertains exclusively to in-browser HTTP transports.",将来将被去掉,因为与传输对象的结合太专有。例子如下:若该项为true,则跳转地址可能将变为http://192.168.0.168/t3.htm#1144207896421,后面的1144207896421就是根据时间戳产生的,若为:changeURL: "aa",则地址将为http://192.168.0.168/t3.htm#aa。
useCache: 布尔类型,默认为false,表示是否将此次请求的结果缓存,以后直接从缓存中获取此次请求的结果。一旦该项为true后,则发出的请求若为以前发过的, 直接从缓存中读取结果。我们可以在bind()执行后,在立刻alert一个东西,当然,要异步执行的。在发出新请求的时候,后面的alert是先执行 的,然后执行请求结果。再发出一样的请求后,由于结果在缓存中,所以直接取请求结果,从而我们会看到alert是在执行完结果后,再执行的。
bindSuccess:布尔类型,默认为false,指明请求是否可以被其它传输对象接受并执行请求。具体用法我也不清楚,以后清楚的时候再补完这里。
下面说说dojo.io.Request对象的几个方法。
上面已经讲了error()和load()方法,这里要注意这2个方法的参数。error(type, errorObject)方法有2个参数,第一个参数表示处理结果,在error()中,永远是"error",第二个参数表示的是传输细节。load(type, data, event)有3个参数,第一位也是处理结果,load()中永远取值为"load",表示处理成功,第二位表示处理成功后返回的信息,第三位表示可以处理传输细节的底层传输对象,官网的一个例子是:当利用dojo.io.XMLHTTPTransport进行传输的时候,第三个参数表示的是对执行请求的XMLHTTP对象的一个引用。
dojo.io.Request对象中,还有2个方法。首先是handle(type, data, event), 可以用来处理所有的情况,如load()、error()和其它一些情况。当type=="load"的时候,三个参数和load()是一样的,当 type=="error"的时候,data就表示error()中的errorObject,而event就无效了。看下面一个用handle()的例 子(摘自官网):  1 dojo.io.bind({
2     url: "http://foo.bar.com/sampleData.txt",
3     handle: function(type, data, evt){
4         if(type == "load"){
5             // do something with the data object
6         }else if(type == "error"){
7             // here, "data" is our error object
8             // respond to the error here
9         }else{
10             // other types of events might get passed, handle them here
11         }
12     },
13     mimetype: "text/plain"
14 });
由上面,可以很容易明白handle()的用法。
dojo.io.Request对象中最后一个方法是abort(),字面意思理解是中止请求,是用来中断执行待处理的请求的。这个行为是用来执行请求的传输对象所拥有的。
在io.js中,可以找到这么一行
dojo.io.transports = [];
由上面bind()查找最合适的可用传输对象的过程,我们知道,这是用来存放已注册的传输对象的。这是一个数组,里面的传输对象将被每一个bind请求所参考,并且第一个传输对象接受一个特殊请求并处理它(这句由于没找个合适的例子,理解还不够透彻,以后补过)。
dojo.io.transports有个addTransport(name)方法,是注册一个可用来处理请求的传输对象,要注意的是name是要在dojo.io 命名空间中的,如我们常用的dojo.io.XMLHTTPTransport,注册时用 dojo.io.transports.addTrasnport("XMLHTTPTransport"),在上面讲dojo.io.Request对 象是也说过,要确认用XMLHttp来执行请求,需指定:transport: "XMLHTTPTransport",正好吻合。
根据上面的资料,transport是可以自己写的,然后定义自己需要的一些东西,具体要怎么写我现在也不清楚,不过个人感觉正如java中的继承那样,能出现相当诱人的结果。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4、Dojo基础
前面说了dojo在ajax方面的一些个东西,感觉要理解透彻还有些dojo内部的东西需要理解,所以想好好看一下dojo的东西,恶补一阵:-)
看了会官方一些个文档,有了些许体会。
dojo.js 被包含进来后,一些对象和函数就可以用了,在用JSEclipse编辑的时候,可以看到一些,不过是包含在dojo.js中的,官网说还包括 boostrap文件里的,我查了下,有bootstrap1.js和bootstrap2.js,不过那些对象直接显示不出来,估计有什么地方要设置, 弄清楚后再补上来。
可用的东东有:
1.dojo.render对象:该对象存放了dojo运行环境的一些信息。
dojo.render.name:根据dojo.render.name = navigator.appName,可以知道这是浏览器的名称,但是直接显示出来是空的,估计还没有被赋值,运行
1 dojo.render.name = navigator.appName;
2 alert(dojo.render.name);
我的出来的是:Microsoft Internet Explorer。
dojo.render.os: 看名字就知道与操作系统有关,事实确实如此。这个属性直接打印出来是[object Object],可以知道是一个对象。查了下源代码,发现这个对象有3个属性:dojo.render.os.osx,当操作系统为"MacOS"取值为 true;dojo.render.os.linux,当操作系统为"Linux"的时候为true;dojo.render.os.win, Windows系统取值为true。3个属性的默认值都为false,一进dojo,则某一个属性被赋值为true,我的Windows系统当然是 dojo.render.os.win为true了。根据源码,若不是这3种系统,dojo.render.os.linux将赋值为true。
dojo.render.ver,官网上说与dojo.version 一样,但我一打印发现不对,查了下代码,发现如下一段:dojo.render.ver = parseFloat(navigator.appVersion, 10),是与浏览器版本号有关,我这里dojo.render.ver的值为4。再找dojo.version,发现这么一段:
dojo.version = {
major: 0, minor: 2, patch: 2, flag: "",
revision: Number("$Rev: 2836 $".match(/[0-9]+/)[0]),
toString: function() {
with (dojo.version) {
return major + "." + minor + "." + patch + flag + " (" + revision + ")";
}
}
};
将dojo.version打印一下,是0.2.2(2836),上面一段的结果。
dojo.render.html,这也是一个对象,它有好几个属性。dojo.render.html.capable,宿主环境是否支持html,一般来说,都是true。其它几个是与浏览器类型有关的,都是布尔类型。dojo.render.html.moz,当浏览器为Mozilla或者Mozilla核心的浏览器(例如 Firefox)时为true; dojo.render.html.safari,使用苹果的Safari浏览器的时候为true,dojo.render.html.ie,平常的机器这个属性都是true,因为我们基本是用Microsoft Internet Explorer,即ie浏览器;dojo.render.html.opera,使用Opera浏览器的时候为true;dojo.render.html.khtml,使用KHTML浏览器(例如Konqueror,但是我还就真没听过这种KHTML浏览器,其它的都知道,看来还是知识不够,唉~)的时候为true。dojo.render.html对象主要是用来判断浏览器类型的。我的机器上dojo.render.html.ie为true。
其它还有dojo.render.svg,dojo.render.vml,dojo.render.swf,dojo.render.swt,由上面的资料,可以知道是对SVG、VML、SWF、SWT的支持,它们都有个capable属性,表示是否支持该技术,ie5.0以上版本支持VML,所以我的dojo.render.vml.capable为true,SVG需要装插件,所以不支持该项,dojo.render.svg.capable为false,dojo.render.swf.capable 也为false,这里的swf不是指flash的swf,而是Simple Web Framework,Simple Web Framework (SWF)是一个基于事件的web框架.它很适合于那些想要开发胖客户端Web应用程序但又不想转向JSF的Struts开发人员。SWF跟Struts 一样也是构建在Jakarta commons基础之上,但使用一个不同的request processor。SWF事件模型支持基于XmlHttpRequest的事件提交。至于dojo.render.swt,不清楚了,难道与java中的SWT有关系?这四个对象的其它属性,在源码中居然没看到相应代码??以后弄明白再回来补过。
2.dojo.version对象。在上面已经讲过这个对象,是dojo库文件的版本,没啥好研究的了。
3.dojo.hostenv对象,个人认为里面的东西很有看头,不过要慢慢讲来也太费时间了,具体可以去看源码中那几个hostenv_XXX.js 文件,dojo.hostenv.getText函数和dojo.hostenv.println函数还有点意思,以后随时补充吧。