作者: Tom Trenka
原文链接: http://dojotoolkit.org/documentation/tutorials/1.6/understanding_widget/
译者: feijia ([email protected])
本教程中,你将会学到Dijit中的_Widget和_WidgetBase这两个重要的基类对象,理解它们作为所有Dojo小部件的基类的作用和功能。
难度:中等
适用Dojo版本: 1.6
创建Dijit (dojo的小部件库) 和自定义小部件的基础,是由两个基类构成的: dijit._widgetBase和 dijit._Widget. 当然Dijit系统还包含其他一些重要的工具例如Dojo的解析器,和Dijit模板系统. 在这个教程里,你将会学到Dijit的基础架构是如何运作的。理解了这些,使用和创建Dijit会变的更加容易。
注意:dijit._Widget 继承自 dijit._WidgetBase; 如果你需要创建自定义的小部件,那么你应当继承 dijit._Widget 而不是dijit._WidgetBase, (当然你也可以直接继承其他Dijit中现有的小部件)
也许你已经注意到了,这两个基类前面都有一个下划线“_" ,这实际上是Dojo中对内部类的一种命名规范。表示这个类不应被用户直接使用,而是作为基类被继承的。
学习Dijit 库,最关键的是要能理解每个小部件的生命周期:从这个小部件被创建,到它能被程序使用,再到最终它被销毁(生命周期也包含了创建与销毁这个小部件所对应的页面中的DOM元素)
每个继承自dijit._Widget的小部件在实例化时都会经历下面的方法调用过程:
([widget].constructor());
[widget].postscript();
[widget].create();
[widget].postMixinProperties();
[widget].buildRendering();
[widget].postCreate(); // 这个方法对开发者而言是最重要的
[widget].startup();
查看示例
从这些方法的名字我们大概可以猜测出它们的作用:
1. 何时初始化参数
2. 创建对应这些参数的图形,DOM元素
3. 确定放置这些元素的位置,
4. 如何处理浏览器相关的一些问题(例如 DOM node measurements)
[widget].postCreate()
对于小部件的开发者来说,postCreate()可能是最重要的一个方法了。
这个方法会在小部件的所有属性参数设置好,小部件所使用的DOM树节点被创建完成后调用。而此时该小部件的DOM节点还没有被添加到主文档中去。因此这个方法是在小部件被呈现给终端用户之前,开发者可以做最多定制的地方。(例如设定许多自定义参数和属性). 这就好像是一出戏的大幕拉开之前,你可以做的最后准备工作的环节. 在开发自定义小部件时,绝大多数定制的属性和行为都会在这里被加入.
[widget].startup()
在Dijit一系列生命周期中,另一个重要方法是启动方法startup. 这个方法会在DOM节点被创建并添加到网页之后执行,同时在这个方法也会等待当前小部件中所包含的子控件被创建并正确启动之后才执行。
注意: 当你用编程的方法创建一个小部件时,记得一定要调用它的startup()方法。很多开发者常犯的错误就是仅仅创建了小部件对象却忘记调用startup(),结果就会导致小部件在页面上无法正确显示。
除了创建和启动,dijit._WidgetBase 还定义了一系列用于析构和销毁的方法:
[widget].destroy();
[widget].destroyDescendants();
[widget].destroyRecursive();
[widget].destroyRendering();
[widget].uninitialize();
在开发自定义小部件时,你需要覆写[widget].uninitialize 方法,在其中释放你所使用的资源. Dijit框架会自动的负责销毁该Widget所使用的DOM节点,以及大部分的对象.
通常来说, Dijit小部件都是一些界面元素,因此多数会包含一些DOM节点。 Dijit._WidgetBase中定义了一个属性domNode ,该属性会指向该小部件中所使用的DOM根节点. 当该小部件的postCreate方法执行后,domNode属性就可以使用了。通过使用这个属性,你可以获取并操纵根节点,例如你可以把这个小部件整体移动到DOM树中的另一个位置上。
除了domNode属性外,有些小部件还会定义一个containerNode属性. 这个属性指定了小部件DOM中的一个容器节点,用来包含子控件。(参看另一篇Dijit模板教程)
_WidgetBase 作为基类,除了提供了一些通用的标准属性,它还定义了一套标准的getter和setter方法来访问开发者自定义属性。这套标准要求你定义一些私有方法(JavaScript中并没有真正的私有方法,Dojo遵循的一种编码规范是使用下划线开头的方法名表示类的私有方法)
// 例如你的小部件中需要对外暴露一个"foo"属性, 则需要定义下列两个私有方法 // custom getter _getFooAttr: function(){ /* do something */ }, // custom setter _setFooAttr: function(value){ /* do something */ }
一旦你定义了上述两个方法,用户就可以使用dijit框架的标准get和set方法来访问你的自定义属性. 例如在定义了上述两个私有方法后,现在用户可以直接使用get和set来访问foo属性。
// 假设这个小部件的实例是"myWidget" // get the value of "foo": var value = myWidget.get("foo"); // set the value of "foo": myWidget.set("foo", someValue);
通过定义Dijit标准的getter和setter方法,不仅规范了编码风格,更重要的是它增加了灵活性。你可以在这些getter和setter方法中加入自定义的其他动作,例如当一个属性被访问时,你可以对DOM节点做相应的修改,或者是发出某些事件的通知信息. 看下面的例子:
// 假设我们需要在Value属性发生变化时触发onChange _setValueAttr: function(value){ this.onChange(this.value, value); //注意这里使用了Dijit标准的_set方法 this._set("value", value); }, // a function designed to work with dojo.connect onChange: function(oldValue, newValue){ }
注意: 在开发小部件时,请务必使用上述的标准getter和setter方法来访问存取自定义属性。另外在实现私有的setter方法时,也务必要使用_set方法。 这是因为通过使用该方法Dijit控件就可以使用Dojo.Stateful中的watch方法。
_WidgetBase还定义了connect和subscribe方法。它们也遵循Dojo中通用的事件和消息订阅机制。因此所有的Dijit小部件都自动了拥有了对事件处理能力:
// 假设我们有一个dijit.Button小部件btn // 我们希望这个button能够监听foo.bar(): btn.connect(foo, "bar", function(){ // 注意这个回调函数的执行上下文是在btn对象中! this.set("something", somethingFromFoo); });
Dijit的主题消息订阅也是类似的。
通过在_WidgetBase中定义connect和subscribe方法,所有的Dijit小部件都有了事件处理功能;另外通过_WidgetBase中定义的connect和subscribe方法,所有的订阅和连接都会被自动记录,因此在小部件销毁时也可以自动的释放和退订相应事件,从而避免了潜在的内存泄露问题。
最后我们来看看_Widget 预定义了哪些属性。
id, // 唯一标记该小部件实例的标识符 lang, // 该小部件所使用的语言标记,可以覆盖Dojo全局的语言设定。 dir, // 双向语言支持的标记(值可以为rtl或者ltr)用来支持阿拉伯语等从右向左显示的语言 class, // domNode(小部件根节点)的HTML class属性 style, // domNode(小部件根节点)的HTML style 属性 title, // HTML title 属性 tooltip, // 可选的指向 dijit.Tooltip 的引用 baseClass, // 小部件的根级别的css类名 srcNodeRef // 在被转换为小部件前的原始DOM节点
然后我们也一起了解一下哪些事件是dijit所支持的,(你可以连接到这些事件,也可以覆写这些事件)
// 鼠标事件 onClick, onDblClick, onMouseMove, onMouseDown, onMouseOut, onMouseOver, onMouseLeave, onMouseEnter, onMouseUp, // 键盘事件 onKeyDown, onKeyPress, onKeyUp, // 其他事件 onFocus, onBlur, onShow, onHide, onClose
本文介绍了Dijit的 _widget 和_WidgetBase 中所提供了基础架构方法和属性,这些共同构成了Dijit框架: 生命周期,DOM'节点引用,自定义属性存取方法getter和setter以及预定义的属性和事件. 有了这些坚实的基础架构,开发一个自己的Dijit小部件也变得很简单。