最近复用十几年前的一个项目,里面用到了dojo框架,so总结一下这个框架的一些重要的知识点
简介
Dojo的全称是Dojo Toolkit,始创于2004年,是当时各种蓬勃发展的JS工具包中的佼佼者。Dojo 为富互联网应用程序(RIA) 的开发提供了完整的端到端的解决方案,包括核心的 JavaScript 库,简单易用的小部件(Widget)系统和一个测试框架。
Dojo特性
Dojo Toolkit 的特性可以分到 4 个不同部分。这种划分使得开发人员可以将库大小保持到最小,确保应用程序性能不受大量 JavaScript 库下载的影响。例如,如果您只需要 Ajax 支持性能,您只需要包含 base 包;不需要包含扩展的 Dijit UI 组件。
Base
Base 包提供 Dojo Toolkit 的基础,包括一些功能,比如 DOM 使用函数、CSS3 基于选择器的 DOM 查询、事件处理、基本的动画、以及 Dojo 基于类的面向对象特性。
Core
Core 包包含一些 Base 中没有包含的附加特性。通常,这些特性不像 Base 中的特性那样频繁使用;因此,它们单独加载减轻 Base 包的负担。从这一点上来讲,Core 包提供一些实际有用的组件,包括高级动画拖放、I/O、数据管理、国际化(i18n)、浏览器历史管理。
Dijit
Dijit 包包含 Dojo 小部件和组件的扩展 UI 库。这些小部件的一些示例包括对话框、日历、调色板、工具提示和树。它也包括一些表单控件,这比标准 HTML 表单控件提供更多的功能,还有一些完整的布局管理选项。
DojoX
Dojo eXtensions (DojoX) 包含工具箱的各个子项目。位于 DojoX 中的大多数是实验特性,但是也有一些稳定组件和特性。
DOM操作方法
dojo.byId
dojo.byId 函数使您可以通过 id 属性选择一个 DOM 节点。该函数是标准document.getElementById 函数的一个别名,但是显然简短易书写。
dojo.query
虽然dojo.byId可以方便的根据id来获取一个DOM节点,但是根据id获取每一个元素几乎是不可能的,因为id是唯一标识。如果一次想获取几个元素,我们可以通过dojo.query方法。
dojo.body
dojo.body方法返回document的body元素
dojo.addOnLoad
现在我们想在window.onload里面处理一点东西,就像Ext.onReady,这个东西在dojo里叫做dojo.addOnLoad。
dojo.create
dojo.create方法创建一个元素
dojo.destroy
dojo.destroy方法会从父元素中删除该元素,并删掉该元素的所有子元素
dojo.empty
dojo.empty方法将删除掉所有子元素
dojo.forEach
dojo.forEach方法允许为数组或NodeList定义一个迭代器,来访问数组或NodeList中的每一项,并提供相应的操作。这个方法会忽略返回值,并且是不支持break的。
事件处理
dojo使用connect方法为DOM添加事件处理函数。
<script type="text/javascript"> function btnClick() { alert("我被点击了"); } var btn = dojo.byId("btn"); dojo.connect(btn, "onclick", btnClick); </script>
复制
如果需要移除一个事件,则需要调用dojo.disconnect方法,这个方法需要一个dojo.connect方法的返回值作为参数:
var handle = dojo.connect(btn, "onclick", btnClick); dojo.disconnect(handle);
复制
订阅/发布
这是dojo很值得关注的一个功能,它使得各个dojo组件可以方便的交互。这个功能使用起来很方便:
<input type="text" name="txtMessage" value="" id="txtMessage" /> <input type="button" name="btn" value="发布…" id="btnPublish" /> <script type="text/javascript"> dojo.addOnLoad(function () { //订阅MesagePublish主题 dojo.subscribe("MessagePublish", function (msg) { alert(msg); }); var btnPublish = dojo.byId("btnPublish"); dojo.connect(btnPublish, "onclick", function () { //发布主题 dojo.publish("MessagePublish", dojo.byId("txtMessage").value); }); }); </script>
复制
如要取消一个订阅,需要使用dojo.unsubscribe方法,这个方法需要一个由dojo.subscribe返回的句柄,其处理方式和dojo.connect、dojo.disconnect 的方式一样。
xmlhttp dojo.xhrGet
发出一个xmlhttp请求
function sayHello() { dojo.xhrGet({ url: "http://localhost/hello/sayHello.jsp", handleAs: "text", load: function(responseText) { alert(responseText); dojo.byId("divHello").innerHTML = responseText; }, error: function(response) { alert("Error"); } }); } dojo.connect(btn,"onclick",sayHello);
复制
handleAs 把获取的内容作为text/html ;load 成功时的回调函数;error 失败时的回调函数
传入参数
var params = { username:'Mark', id:'105' } dojo.xhrGet({ url: "http://localhost/hello/sayHello.jsp", content:params, //... });
复制
注意那个content参数,你要传入的参数是个关联数组/object,dojo会自动把参数解析出来,要使用post方法? dojo.xhrGet ---> dojo.xhrPost ,其他的还有,dojo.xhrPut、dojo.xhrDelete。
dojo.hitch
既然用到了xmlhttp,一个常见的问题就是回调函数的scope/context。在prototype、mootools里我们常用Function.bind,在dojo中,做相同事情的东西叫做dojo.hitch。
var handler = { name:'Mark', execute1: function(){ dojo.xhrGet({ url: "http://localhost/hello/sayHello.jsp", handleAs: "text", error: function(text) { console.dir(this); alert(this.name);//输出undefined,这里的this表示当前io参数 } //... }); }, load: function(text){ alert(this.name); }, execute2: function(){ dojo.xhrGet({ url: "http://localhost/hello/sayHello.jsp", handleAs: "text", error: dojo.hitch(this,"load") //输出Mark //error: dojo.hitch(this,this.load); //与上一句相同,知道为什么要用方法名字而不是引用了吧?省去了长长的一串this.xxx //... }); } }
复制
面向对象
(1)定义Class
dojo.declare("Customer",null,{ constructor:function(name){ this.name = name; }, say:function(){ alert("Hello " + this.name); }, getDiscount:function(){ alert("Discount is 1.0"); } }); var customer1 = new Customer("Mark"); customer1.say();
复制
(2)继承
dojo.declare("VIP",Customer,{ getDiscount:function(){ alert("Discount is 0.8"); } }); var vip = new VIP("Mark"); vip.say(); vip.getDiscount();
复制
(3)this.inherited方法调用父类
dojo.declare("VIP",Customer,{ getDiscount:function(){ this.inherited(arguments); //this.inherited("getDiscount",arguments); } });
复制
(4)关于构造函数
constructor:function(args){ var args = args || {}; this.name = args.name; this.age = args.age; }
复制
(5)多继承,mixin
dojo.declare("Customer",null,{ say:function(){ alert("Hello Customer"); }, getDiscount:function(){ alert("Discount in Customer"); } }); dojo.declare("MixinClass",null,{ say:function(){ alert("Hello mixin"); }, foo:function(){ alert("foo in MixinClass"); } }); dojo.declare("VIP",[Customer,MixinClass],{ }); var vip = new VIP(); vip.getDiscount(); vip.foo(); vip.say();//输出"Hello MixinClass"
复制
子类自动获得父类和mixin的所有方法,后面的mixin的同名方法覆盖前面的方法。
package机制
(1)dojo.require
dojo.require就是引入相应路径文件下的js文件。现在我们假设要用project/dojo-lib/dojo/string.js
dojo中的顶层目录就是dojo.js所在目录的上一层,即"project/dojo-lib/",而dojo.js放在project/dojo-lib/dojo/dojo.js 所以我们就这样:
dojo.require("dojo.string");
(2)dojo.provide
要自己编写一个package怎么办,那就利用dojo.provide。比如要写在:project/dojo-lib/com/javaeye/fyting/Package1.js 那么在对应的Package1.js中第一行需要这样写:
dojo.provide("com.javaeye.fyting.Package1");
(3)dojo.registerModulePath
那要是我写的js文件不想和dojo放在一起怎么办呢,那就用registerModulePath。假设要放在:
project/js/com/javaeye/fyting/Package2.js
Package2.js和上面的Package1.js一样的写法,不需要作特殊变化,就这样就行:
dojo.provide("com.javaeye.fyting.Package2");
在使用时,需要指名这个Package2.js所在的位置,
dojo.registerModulePath("com","../../js/com");
只需要注意这里的相对路径是相对dojo.js来的。
我们假设所有以com.javaeye开头的js都放在一起,而com.microsoft的放在另外的地方,为了防止冲突,可以这样:
dojo.registerModulePath("com.javaeye","../../js/com/javaeye");
dojo.registerModulePath("com.microsoft","../../javascript/com/microsoft");