Extjs 事件机制


Ext版本:4.2.1


  1. 从原生的浏览器事件处理开始


HTML元素如下:

<input id ='test' type='button' value='click me'>


事件处理如下:

/** 浏览器原生的事件机制 */
var e = document.getElementById("test");
// DOM 0 事件覆盖
e.onclick = function(event) { console.log("dom 0, event 1, event type: " + event.type); };
e.onclick = function(event) { console.log("dom 0, event 2, event type: " + event.type); };
// DOM 2 支持多处理事件
e.addEventListener("click",function(event){
	console.log("dom 2, event 1, event type: " + event.type);
},false);
e.addEventListener("click",function(event){
	console.log("dom 2, event 2, event type: " + event.type);
},false);



2. 采用Ext对DOM和浏览器封装的处理方式

/** Ext封装了DOM和Event事件 */
var test = Ext.get("test");

/**
* 注意这里调用的是Ext.EventManager的on()方法
* 而不是后面例子中Observer中的on()方法
* 但是二者类似
*
* Ext.dom.Element中定义了on()方法,会调用Ext.EventManager的on()
*/
test.on("click", function () {
	console.log("ext handler1");
});
test.on("click", function () {
	console.log("ext handler2");
});

每一个Element继承自Ext.dom.Element,也就有了on()方法,会调用EventManager的on()方法,进行事件处理。



3. Ext对用户自定义事件的处理方法


3.1 类级别的事件监听和处理


在类中,定义监听事件,在每个对象中一旦触发此事件,则执行处理方法,类代码如下:

Ext.define('Application.SportsMan',{
	config:{
		userName: ''
	},
	extend: "Ext.util.Observable", // 关键是这里
	constructor: function(c) {
		var me = this;
		
		/** 添加事件名称或者定义事件名称
		*	是不是多余?addListener已经有了事件名称了,但是的确可以省略
		*	
		*   不多余,起到统一声明的作用
		*/
		me.addEvents('sing');
		me.addEvents('run');
		
		/** 
		* 注意必须在addListener之前调用callParent 
		* 因为在Observable的构造方法constructor中要进行一些初始化操作
		*/
		me.callParent(arguments);
		/** 1 对类添加监听事件 */
		this.addListener('sing',function(milk){  
			console.log(me.getUserName() + ' is singing..');
		}); 
		
		/** 定义多个处理事件 */
		this.addListener('sing',function(milk){  
			console.log('note: ' + me.getUserName() + ' is singing again ..');
		}); 
		
		this.addListener('run',function(milk){  
			console.log(me.getUserName() + ' is running..');
		});
	}
});


测试代码如下:

/** 类的加载方式1
var lx = Ext.create('Application.SportsMan',{});
lx.setUserName('liu xiang');
lx.fireEvent('run');

var ym = Ext.create('Application.SportsMan',{});
ym.setUserName('yao ming');
ym.fireEvent('sing');
*/

/** 类的加载方式2 */
Ext.require('Application.SportsMan',function(SportsMan){
	var lx = new SportsMan();
	lx.setUserName('liu xiang');
	lx.fireEvent('run');
	
	var ym = new SportsMan();
	ym.setUserName('yao ming');
	ym.fireEvent('sing');
});


执行结果如下图:

wKiom1TK1pCSbSTdAAC9Vj59RdA266.jpg




3.2 对象级别的事件监听处理


在每个对象实例化之后,分别指定监听事件。和类的处理方式差别在于,同一个类的不同对象对同一事件可以有不同的处理方式。


类代码如下:

Ext.define('Application.Car',{
	config:{
		carName: ''
	},
	extend: "Ext.util.Observable",
	constructor: function(c) {
		var me = this;
		
		/** 
		* 同样这里的addEvents()可以省略
		* 好处是:统一的对外声明监听方法的名称
		*/
		me.addEvents('sing');
		me.addEvents('run');
		
		me.callParent(arguments);
	}
});


测试代码如下:

Ext.require('Application.Car',function(Car){
	var bc = new Car();
	/** 2 给对象绑定事件监听 */
	bc.on('run',function(){ // on 是addListener的简写
		/**
		* 这里的getCarName()在方法setCarName() 调用之前
		* 也没有问题,因为是回调函数
		*/
		console.log(bc.getCarName() + ' is running..'); 
	});
	bc.on('run',function(){
		console.log('note: ' + bc.getCarName() + ' run far away..');
	});
	
	bc.setCarName('ben chi');
	bc.fireEvent('run');
	
	var bmw = new Car();
	bmw.addListener('sing',function(){
		console.log(bmw.getCarName() + ' is singing..');
	});
	bmw.setCarName('bao ma');
	bmw.fireEvent('sing');
	
	
	/** 3 在对象的配置项中,定义监听 */
	var pt = new Car({
		carName: 'Phaeton',
		listeners:{
			run: function(){
				console.log(this.getCarName() + ' is running..');
			}
		}
	});
	pt.fireEvent('run');
});

给对象定义事件监听,可以在实例化之后调用on()方法,也可以直接定义在配置项listeners中。



小结:类或者对象的事件监听处理,比较简单。

  1. 继承Observable,这样就继承了addEvent()等方法,这里的Observable就是一个bus,是类级别的bus

  2. 调用addEvent('abc')相当于是向bus中添加了一个空对象'abc'

  3. 调用addListener('abc',function)相当于是在bus中找到对应的对象'abc',给他添加一个回调方法,如果没有则addEvent('abc')

  4. 调用fireEvent('abc')会在bus中找到对象'abc',然后挨个执行回调函数



所以,这里采用的是观察者(Observer)模式,所有继承自Observable的类都是Subject,在它上面注册(addListener)的每一个事件Event,都是观察者Observer!其实这里是Observable和Event的关系。



4. MVC结构中,Controller对Component、Store等的监听


这里涉及一个很重要的类 Ext.app.EventDomain。他把监听的范围从类级别扩大到类型级别,并且这个类型是可以自定义的。查找的时候,会在这个类型下面去查找。


  • 定义了全局的bus ={},存放每个监听事件,结构类似于{run: [{Car: [Student,Teacher]},{Animal: [Student,Teacher]}]}

当Student类中对Student类的Car方法设置(listen方法)了监听事件的时候,会在bus 中添加一条记录

  • monitor方法,改变某个类的prototype的fireEventArgs方法。也就是重写了这个类从Observable中继承过来的fireEventArgs

  • 当Car.fireEvent('run')的时候,会调用上面的fireEventArgs,会调用dispatch()方法

  • dispatch()方法,会根据方法名字'run'在bus中找到事件对象,然后根据target,也就是Car查找对应的Controllers,也就是[Student,Teacher],然后执行每个Controller中的处理方法


举例如下:


被监听类:

Ext.define('Application.FooC', {
    extend: 'Ext.app.Controller',
	id: 'FooC',
	init: function(){
		this.fireEvent('barOpenWindow', 'Foo');
	}
	
});


监听类1:

Ext.define('Application.BarC', {
    extend: 'Ext.app.Controller',
	id: 'BarC',
    init: function() {
        this.listen({
            controller: { // 详见Ext.app.domain.Controller
                '#FooC': {      
                    barOpenWindow: 'openWindow'
                }
            }
        });
    },
 
    openWindow: function(who) {
        console.log(who + ' is coming..');
    }
});


监听类2:

Ext.define('Application.BzzC', {
    extend: 'Ext.app.Controller',
	id: 'BzzC',
    init: function() {
        this.listen({
            controller: { // 注释1 这就是分类
                '#FooC': {      
                    barOpenWindow: 'openWindow'
                }
            }
        });
    },
 
    openWindow: function(who) {
        console.log(who + ' is coming..');
    }
});



类之间的结构关系:


  • 所有的Controller都继承自Ext.app.Controller,而它use了Ext.app.domain.Controller

  • Ext.app.domain.Controller是单例的

  • Ext.app.domain.Controller继承自Ext.app.EventDomain,并且定义type:'controller',这个就是注释1的关键字

  • Ext.app.domain.Controller还定义了idProperty: 'id',在match匹配的时候,会按照id匹配,所以Controller是按照id查找的

  • Ext.app.domain.Controller在构造的时候,会调用me.monitor(Ext.app.Controller);也就是Ext.app.EventDomain的monitor()方法


所以,这种模式是中介者(Mediator)模式,我们的Controller都是colleague,而domain.Controller就是Mediator!



monitor代码如下:

monitor: function(observable) {
        var domain = this,
            prototype = observable.isInstance ? observable : observable.prototype,
            fireEventArgs = prototype.fireEventArgs;

        domain.monitoredClasses.push(observable);

        prototype.fireEventArgs = function(ev, args) { // 这里的原型就是observable
            var ret = fireEventArgs.apply(this, arguments);

            if (ret !== false) {
                ret = domain.dispatch(this, ev, args); // 当fireEvent的时候,执行
            }

            return ret;
        };
    }



dispath代码如下:

dispatch: function(target, ev, args) { // 注意:target 是被监听类
        var me = this,
            bus = me.bus, // 见下面1图
            selectors = bus[ev],
            selector, controllers, id, events, event, i, ln;

        if (!selectors) {
            return true;
        }

        // Loop over all the selectors that are bound to this event
        for (selector in selectors) {
            // Check if the target matches the selector
            if (selectors.hasOwnProperty(selector) && me.match(target, selector)) {
                // Loop over all the controllers that are bound to this selector
                controllers = selectors[selector];

                for (id in controllers) {
                    if (controllers.hasOwnProperty(id)) {
                        // Loop over all the events that are bound to this selector
                        events = controllers[id];

                        for (i = 0, ln = events.length; i < ln; i++) {
                            event = events[i];

                            // Fire the event!
                            if (event.fire.apply(event, args) === false) {
                                return false;
                            }
                        }
                    }
                }
            }
        }

        return true;
    }


wKioL1TK6D_gQQldAAE39S7dXR4541.jpg

你可能感兴趣的:(ExtJs,event,Observable)