近日,看了玉伯寫的《構建前端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(){
//設定各元件共用介面
}
})