node-debug.js :
Node 为 HTMLElement包装(处理了缓存),关键 addMethod ,importMethod 将 Y.DOM 中的静态方法(YUI2模型)迁移到 Node实例中去.
Y.DOM.method(node,args); ==> node.method(args);
NodeList 为 多个 htmlelements的包装,关键也为 NodeList.addMethod 以及NodeList.importMethod,同Ext .compositeElement 类似
将Node的方法也加到NodeList中去,并调用时自动循环对nodelist中的所有元素调用对应方法。
event-custom-debug.js & event-simulate-debug.js
自定义事件处理系统和原生dom处理事件系统完全接口一致(不同于Extjs,node和组件的事件处理完全分离,yui3 Node和组件其实都是 augment 自 EventTarget):除了dom模拟触发需要 node.simulate 而自定义事件需要obj.fire。
YUI当前实例 Y 本身也 augment 自EventTarget,一些全局自定义事件比如windowresize,domready即由 Y触发,可以在Y上处理。
注意 node.simulate并没有采用 jquery自己遍历树结构,挨个执行处理函数的方式,而是利用了浏览器自身的event simulation framework,对于w3c-dom-event2兼容浏览器使用document.createEvent而对于ie则使用document.createEventObject
而dom事件通过 DOMEventFacade 来进行包装,保证各个浏览器的一致性。当emitFacade为true时复杂情况下的自定义事件触发使用 EventFacade 来包装,具有和DOMEventFacade基本一致的属性,EventFacade.detail为fire参数。
事件处理器统一用Subscriber包装,可以方便实现AOP,contextFn动态上下文环境的调用。
并且自定义事件也实现了bubble以及defaultFn功能 (publish必须设置emitFacade为true,调用fireComplex ) ,可方便的实现以前组件必须显式检查beforeShow以及调用者必须监听 beforeShow 等事件来实现AOP拦截,现在只需组件编写者申明事件的defaultFn以及调用者监听 on("show")决定是否preventDefault即可,bubble定义在 EventTarget 中,EventTarget可以通过addTarget添加事件往上传播的通知对象,使得自定义事件也可以通过delegate来简化事件处理。
EventTarget 还另外支持了 jquery 类似的分类事件功能,对同一个事件的一些监听器可以分做一类,类名放在事件名前面用|分隔,以及结合 Base 支持的前缀机制,可以解决不同类的同名事件监听问题,使用例子 。
event-debug.js
不同于dom-event2中添加多个事件处理器由浏览器调度,yui3以及ext每个事件每个元素底层只绑定一个dom2事件处理器,由这个dom2事件处理器再调用绑定在这个事件的所有事件处理函数。
Y.Env.evt.dom_wrappers 为对所有事件的统一处理封装,将每个dom事件映射为自定义事件,每个自定义事件有一个事件处理器绑定到dom节点,该事件处理器再调用所有用户绑定的事件处理函数。
20100427感悟:
事实上 Node.on 调用 Y.on ,而 Y argument 自 EventTarget ,则 相当于在 Y 上添加自定义事件,不同点在于,在Y上添加自定义事件的同时还在dom node上绑定事件当dom事件发生时fire自定义事件,自定义事件fire再调用我们的处理函数,又一次实现了自定义事件与dom事件的统一。
键:
var ek = Y.stamp(el), key = 'event:' + ek + type, cewrapper; if (false === facade) { key += 'native'; } if (capture) { key += 'capture'; }
值 :
cewrapper,类型为 CustomEvent ,这样就和自定义事件完美融合在一起,将dom事件映射到了自定义事件,一个CustomEvent 对应于多个事件处理函数(listeners)
详见函数Y.Event._createWrapper: cewrapper 。
对单个元素单个事件的统一处理,事件发生后 cewrapper.fn 触发,然后再取Y.Env.evt.dom_map 为元素和该 cewrapper 关联的事件处理函数(listeners),再统一依次调用。
精妙的Y.augment :
保证augment后,new出来的示例,在运行相关augment方法时,之前augment来源构造器要在当前实例上运行且只运行一次!
关键源码解析:
Y.each(sProto, function(v, k) { replacements[k] = function() { //任何一函数一运行,就把实例上的所有override函数置为 override来源的原方法 for (i in sequestered) { if (sequestered.hasOwnProperty(i) && (this[i] === replacements[i])) { // Y.log('... restoring ' + k); this[i] = sequestered[i]; } } //保证只会运行一次,不管调用哪个override的方法,一旦调用,this[k]就不是replacements[i]了, construct.apply(this, a); return sequestered[k].apply(this, arguments); }; if ((!wl || (k in wl)) && (ov || !(k in this))) { if (L.isFunction(v)) { //保留override来源的最原始方法 sequestered[k] = v; //为了保证构造器运行且只运行一次,需要暂时替换一下 this[k] = replacements[k]; } else { this[k] = v; } } }, newProto, true);
示例详解:
var comp=function(){}; Y.augment(comp,Y.EventTarget); var two=new comp(); //EventTarget构造函数没有执行 console.log(two._yuievt); //方法为暂时替代方法 console.log(two.on.toString()); //运行一次override函数,EventTarget构造函数作用在实例上,且实例上的所有方法复原 two.on("ok",function(){}); //一切正常 console.log(two._yuievt); console.log(two.on.toString());
总之一句话:yui任何一个函数的调用都很曲折,为了完美的架构以及桥梁承接以前略显陈旧的代码,yui3在性能上确实做出了不少的牺牲。
参考:
YUI3源码 以及 Luke Smith — Events Evolved
Satyen Desai — YUI3: Design Goals and Architecture