近日,看了玉伯写的《构建前端UI组件的新思路》一文,让我追忆起去年自己分享的一篇P文《随感协同开发的JS设计模式》 ,有几分共鸣……
话说去年支付宝新版收银台项目中,我就小试了一把这种组件编码模式,点滴心得,这里和大家做一个交流:
回顾一下之前说到的抽象类,对设计模式有所了解的同学可能会觉得有些眼熟,没错,初一看,觉得它很像一个抽象工厂,但是结合下面的基础类来看,你会发觉我并没有在各基础类中,重写getVessel,show,hide等方法,而是直接继承了抽象类中的这些方法。一定会有人不解为什么要这么做,无他,就因为他是JS,而非JAVA。一定的偶合度换来足够的灵活在我看来一点都不过分,更何况这个抽象类是必须确保绝对稳定的,他在成形后不允许被随意修改那是必须的。
通过这个抽象类,我把一个触点对象和一容器对象做了关联,通过action方法去实现他们之间最简单的交互行为。“最简单”,那么无非就是显示或者隐藏之类的操作了,所以我又定义了show,hide方法。很明显“最简单”的交互行为没办法满足100%的用户行为,所以我必须设置一个setInterface方法,在需要特殊交互效果的类中去添加效果类。最后避免大家使用时直接实例化这个抽象类,在action方法中提醒大家,如要实例化操作,请到特定的继承类中去重写action方法。
通过这个抽象类,我们可以继承出最基础的aPop,dropDown,xbox,xTab等组件…这些上篇p文中已经有提到,这里不多说,着重解释一下在这写基础类满足不了特殊需求时,我们该如何快速的进行个性化组件开发。
下面以xTab为例,我们可以用这个组件完成基础的多触点多容器之间的切换效果,但是如果在这基础上需要再加一些动画效果又该怎么办呢?先看看继承类tab的实现代码:
可以发现我在show方法之后执行了setInterface方法,这里默认会调用抽象类中的同名方法,这个接口方法是为同类交互行添加附加的交互效果类而设的。打个比方:现在要处理一个slideTab的效果,那么我们只需要基于xTab做一个应用类的继承,覆盖setInterface方法,添加一个实现slide效果的动画类就ok了!
AP.widget.animTab = AP.widget.xTab.extend({
setInterface:function(target,vessel){
this.parent(target,vessel);
this.anim(vessel);
},
anim:function(vessel){
...
}
});
说实在的,这是挺土的一种设计思路,不过可以让我们换一种角度去思考组件的编码模式,以上只是粗浅的一些应用尝试,精彩还会继续…waiting for you!
AP.widget.xTab = AP.widget.basic.extend({
bindEvents:function(target,vessel){
E.on(target,this.options.eventType,this.action,target,this);
E.on(window,'load',this.oXtab,target,this);
},
action:function(e,target){
this.switchTab(e,target);
},
switchTab:function(e,target){
...
for(i=0,len=tabs.length;i<len;i++){
var hash = tabs[i].href.split("#")[1];
var vessel = D.get(hash + 'Extend');
if(vessel){
this.hide(vessel);
}
D.removeClass(tabs[i].parentNode,'current');
if(target.href == tabs[i].href){
D.addClass(target.parentNode,'current');
if(vessel){
this.show(vessel);
}
//设置各类应用接口
this.setInterface(target,vessel);
}
E.preventDefault(e);
}
},
showTab: function(index){
...
},
//初始化定位tab
oXtab:function(target,e){
...
}
});
AP.widget.basic = new AP.Class({
setOptions:function(options){
//接口设置
},
initialize:function(targets,options){
//初始化方法,目的是建立targets子集元素和某方法的关联
},
getVessel:function(target){
//获取满足target映射关系的容器
},
bindEvents:function(target,vessel){
//这里绑定target的触发动作
},
action:function(){
//target绑定的事件触发的执行函数,包含你要执行的逻辑
},
show:function(){
//显示容器
},
hide:function(){
//隐藏容器
},
setInterface:function(){
//设置各组件共用接口
}
})