Ext架构分析(1)--理解Ext.util.Event
原文链接 http://webtec.blog.ccidnet.com/blog-htm-do-showone-uid-77419-type-blog-itemid-246149.html
由于Ext2.0中所有的组件都是由Observable继承而来,理解Ext就需要先从Ext.util.Observable说起,而Observable是对Event对象进行管理,从而理解Observable必须首先从Ext.util.Event说起。
Ext.util.Event是一个封装的非常精致的对象,但和你想象的不同,Event同任何的HTML DOM元素没有任何的关系(尽管Ext是处理HTML元素的),实际上,它是一个通用的事件及其事件的处理的对象。 也许有朋友要问,HTML Element本身已经支持了事件,为什么还要再重新设计一套Event机制呢?其实,基本上所有的javascript框架都会实现自己的Event机制,原因很多,但是至少有一点:HTML元素对于事件的处理是通过简单的单一绑定实现,也就是说,如果不进行任何的封装,你的事件只能唯一的绑定到一个事件处理句柄,举个例子:
var e=document.getElementById("test");
e.onclick=function(){alert("handle1")};
e.onclick=function(){alert("handle2")};
运行的结果你回发现,只会弹出一个"handle2"的alert框,因为第一个事件已经被第二个事件重载了。而使用Ext框架,你可以放心的解决这个问题,同一个事件可以依次被绑定到多个事件处理句柄上。
Ext.util.Event对象构建器需要传入两个对象:obj(处理事件的缺省对象),name(事件名称)。在构建Event对象时,Event对象会同时构建一个事件的处理函数的数组:listeners,通过这个数组实现了一个事件的多个事件句柄函数的处理。
Ext.util.Event = function(obj, name){
this.name = name;
this.obj = obj;
this.listeners = [];
};
通过Event的fire方法就可以依次触发该数组中的处理函数。 实际上,fire方法在遍历listeners数组中的处理函数过程中,只要处理函数的返回值为false,则不再继续运行后续的处理函数。所以,可以通过检查fire方法的返回值得知事件处理函数是否完全被运行。
fire : function(){
var ls = this.listeners, scope, len = ls.length;
if(len > 0){
this.firing = true;//通过firing可以保证多个事件处理函数不会并发运行
var args = Array.prototype.slice.call(arguments, 0);//slice方法可以有效的进行数组的克隆
for(var i = 0; i < len; i++){
var l = ls;
//事件的处理,只要有一个处理函数返回false,整个事件处理就被停止
if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
this.firing = false;
return false;
}
}
this.firing = false;
}
return true;
}
Event可以通过addListener、removeListener、clearListeners(移除所有的事件处理函数)方法对 listeners进行管理。但是,Listener中保存的事件处理函数实际上并不是addListener传递的函数,实际上,addListener传入的方法通过createListener对事件的处理函数进行了封装,通过封装,实现了对通一个重复事件的的三种不同处理方式:delay(延迟运行)、single(移除Listener中的处理函数,仅运行当前的处理函数)、buffer(避免重复运行处理函数)。
createListener : function(fn, scope, o){
o = o || {};
scope = scope || this.obj;
var l = {fn: fn, scope: scope, options: o};
var h = fn;
if(o.delay){
h = createDelayed(h, o, scope);
}
if(o.single){
h = createSingle(h, this, fn, scope);
}
if(o.buffer){
h = createBuffered(h, o, scope);
}
l.fireFn = h;
return l;
}
var createBuffered = function(h, o, scope){
var task = new Ext.util.DelayedTask();
return function(){
task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
};
};
var createSingle = function(h, e, fn, scope){
return function(){
e.removeListener(fn, scope);
return h.apply(scope, arguments);
};
};
var createDelayed = function(h, o, scope){
return function(){
var args = Array.prototype.slice.call(arguments, 0);
setTimeout(function(){
h.apply(scope, args);
}, o.delay || 10);
};
};
Ext架构分析(2)--理解Ext.util.Observable
Observable维护了一个events数组,并提供了更加方便的对于事件的封装和调用机制。同Event一样,它也提供了addListener、 removeListener方法。它提供的addListenere方法使用起来更加方便,你可以通过json对象一次实现多个事件的绑定:
foo.addListener({
'click' : {
fn: this.onClick,
scope: this,
delay: 100
},
'mouseover' : {
fn: this.onMouseOver,
scope: this
},
'mouseout' : {
fn: this.onMouseOut,
scope: this
}
})
如果你看一下源程序,你会发现,实际上,observable最终还是把事件绑定机制委托到Event对象上:
addListener : function(eventName, fn, scope, o){
//如果参数是一个json对象
if(typeof eventName == "object"){
o = eventName;
for(var e in o){
if(this.filterOptRe.test(e)){
continue;
}
if(typeof o[e] == "function"){
// shared options
this.addListener(e, o[e], o.scope, o);
}else{
// individual options
this.addListener(e, o[e].fn, o[e].scope, o[e]);
}
}
return;
}
o = (!o || typeof o == "boolean") ? {} : o;
eventName = eventName.toLowerCase();
var ce = this.events[eventName] || true;
if(typeof ce == "boolean"){
//事件不存在则新建一个Event对象并把它纳入event数组
ce = new Ext.util.Event(this, eventName);
this.events[eventName] = ce;
}
//调用event的addListener方法
ce.addListener(fn, scope, o);
}
除了支持addListener方法,Obserable还提供了一个addEvent方法,通过该方法,Observable可以绑定多个不包含处理句柄的Event对象:
addEvents : function(o){
if(!this.events){
this.events = {};
}
if(typeof o == 'string'){
for(var i = 0, a = arguments, v; v = a; i++){
if(!this.events[a]){
o[a] = true;
}
}
}else{
Ext.applyIf(this.events, o);
}
},
为了方便使用,observable对addListener和removeListener提供了一个更加简洁的所写:on和un:
Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
Ext.util.Observable.prototype.un = Ext.util.Observable.prototype.removeListener;
你可以通过suspendEvents和resumeEvents方法随时对于事件进行暂停和继续:
suspendEvents : function(){
this.eventsSuspended = true;
},
resumeEvents : function(){
this.eventsSuspended = false;
},
当然,通过fireEvent方法,你可以触发制定的事件:
fireEvent : function(){
if(this.eventsSuspended !== true){
//arguments[0]就是你需要触发的事件
var ce = this.events[arguments[0].toLowerCase()];
if(typeof ce == "object"){
return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
}
}
return true;
},
Observable还通过capture和releaseCapture提供了对于事件处理函数的拦截机制:
Ext.util.Observable.capture = function(o, fn, scope){
o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
};
Ext.util.Observable.releaseCapture = function(o){
o.fireEvent = Ext.util.Observable.prototype.fireEvent;
};
Ext架构分析(3)--Widget之父Component:总结
在这里,我们引用Ext Overview中的Component life cycle对组件的功能进行相应的总结:
配置项对象生效:
组件对象的构造器会把全部的配置项传入到其子类中去,并且进行下列所有的步骤。
组件的底层事件创建
这些事件由组件对象负责触发。事件有enable, disable, beforeshow, show, beforehide, hide, beforerender, render, beforedestroy, destroy
组件在组件管理器里登记
initComponent这方法总是使用在子类中,就其本身而论,该方法是一个模板方法(template method),用于每个子类去现实任何所需的构造器逻辑(any needed constructor logic)。首先会创建类,然后组件对象各层次里面的每个类都应该调用superclass.initComponent。通过该方法,就可方便地实现(implement),或重写(Override)任意一层构造器的逻辑。
加载插件(如果有的话)
如果该组件有指定任何插件,这时便会初始化。
渲染组件(如果必须的话)
如果指定了组件的renderTo 或 applyTo配置属性,那么渲染工作就会立即开始,否则意味着延时渲染(在layout对象的layout方法中也会对组件进行渲染),即等待到显式控制显示,或是其容器告知其显示的命令。
渲染过程 Rendering
触发beforerender事件
这是个可取消的事件,指定的句柄(handler)通过返回false可阻止组件进行渲染
设置好容器
如果没有指定一个容器,那么将使用位于DOM元素中组件的父节点作为容器。
调用onRender方法 这是子类渲染最重要的一个步骤,由于该方法是一个模板方法(template method),用于每个子类去现实任何所需的渲染逻辑(any needed render logic)。首先会创建类,然后组件对象各层次里面的每个类都应调用superclass.onRender。通过该方法,就可方便地实现(implement),或重写(Override)任意一层渲染的逻辑。
处理组件是“隐藏”状态 默认下,许多组件是由CSS样式类如"x-hidden"设置隐藏的。如果 autoShow所配置的值为true,这时就不会有这个"hide"样式类作用在该组件上
自定义的类、样式生效 Custom class and/or style applied
一切组件的子类都支持cls和style 两种特殊的配置属性,分别用于指定用户自定义的CSS样式类和CSS规则。 推荐指定cls的方法来制定组件及其各部分的可视化设置。由于该样式类会套用在组件markup最外的一层元素,所以标准CSS规则会继承到组件下任何子元素的身上。
触发render事件 The render event is fired
这是组件通知成功渲染的一个步骤。这时,你可肯定地认为组件的DOM元素是可用的了。如果尝试在渲染之前访问组件,会抛出一个不可用的异常。
调用了afterRender方法 The afterRender method is called
这是另外一个实现或重写特定所需的“后渲染”逻辑的模板方法。每个子类应调用superclass.afterRender.
组件被隐藏或禁用(如果有的话) The Component is hidden and/or disabled (if applicable)
配置项hidden和disabled到这步生效
所有状态感知的事件初始化(如果有的话) Any state-specific events are initialized (if applicable)
状态感知的组件可由事件声明特殊加载和保存状态。如支持,加入此类的事件。