看了 NCZ 的 scalable-javascript-application-architecture 后对YUI3的沙盒设计以及模块管理有了更深的理解,尤其其中的模块交互方式非常有趣。
传统模式:
以前使用ext,jquery的多模块交互模式:
页面两个模块 m ,m2 ,当 m 变化时需要通知 m2 更新自己的相关状态。
var m=new Module1(); var m2=new Module2(); m.on("change",function(e){ m2.update(...); }); m.fire("change","hello");
耦合问题:
由以上代码可见,m ,m2实际上已经耦合在一起,它们必须出现在页面的相同位置,并且如果需求变化去除了m2,则m1.on也必须去除。
YUI3事件解耦方式:
YUI3事件引入了bubble以及broadcast模式,尤其broadcast,可以将事件传递到全局事件空间,其他模块只要监听全局事件即可,去除了和某一模块的耦合性。
修正版:
YUI({ filter:"DEBUG" }).use("base","event","node","event-simulate","event-custom","node-event-simulate","plugin","oop","pluginhost",function(Y){ function Module1(){ Module1.superclass.constructor.call(this); this.publish("change",{ emitFacade:true, //该事件需要bubble到全局空间 broadcast:2 }); } Module1.NAME="Module1"; Y.extend(Module1,Y.Base); var change=Y.one("#trigger"); var m=new Module1(); change.on("click",function(){ m.fire("change","hello"); }); }); YUI({ filter:"DEBUG" }).use("base","event","node","event-simulate","event-custom","node-event-simulate","plugin","oop","pluginhost",function(Y){ function Module2(){ Module2.superclass.constructor.call(this); } Y.extend(Module2,Y.Base,{ update:function(args){ alert("Module2 knowns Module1's change :"+args); } }); var m=new Module2(); //另一模块只需监听某模块发布到全局空间的change事件,并更新自己状态 Y.Global.on("Module1:change",function(e){ m.update(e.details); }); });
则可见 模块1,模块2边界非常清晰,可以根据需求变化随意去除任一模块,系统更具灵活性。
PS:事件的前缀机制
首先可以看事件机制的详细解释 ,在上述例子中,我们看到了 Global监听的事件为Module1:change,而不是普通的change,而这个Module1正是module1类的名字配置,EventTarget中有prefix的机制,但是平常直接augment,EventTarget时这个机制是没有对应开启功能,当从Base继承时,Base初始化自己的事件机制的prefix为配置的类名:
//init: this._yuievt.config.prefix = this._eventPrefix=this.constructor.EVENT_PREFIX || this.constructor.NAME;;
那么在 EventTarget功能代码中,每次进行on以及fire都会检查是否存在prefix,如果绑定的参数为eventName形式,会被补全为prefix:eventName的形式,prefix默认为自己的prefix
var parts = _parseType(type, this._yuievt.config.prefix) //加入前缀标识 type=parts[1];
可见如果单单监听change事件,则实际效果为监听自身的change,而如果要监听某类对象的change,这正是prefix发挥余地的地方,也避免了多类对象同名事件的混淆。
参考资料:
2010-10-21 新瓶老酒:managing javascript object