大熊君JavaScript插件化開發------(實戰篇之DXJ UI ------ ItemSelector重構完結版)

一,開篇分析javascript

Hi,你們好!大熊君又和你們見面了,還記得上一篇文章嗎。主要講述了以「jQuery的方式如何開發插件」,以及過程化設計與面向對象思想設計相結合的方式是css

如何設計一個插件的,兩種方式各有利弊取長補短,本系列文章是以學習爲導向的,具體場景你們本身定奪使用方式。那麼今天這篇文章咱們說點什麼那?嘿嘿嘿html

。咱們接着上篇文章對不足的地方進行重構,以深刻淺出的方式來逐步分析,讓你們有一個按部就班提升的過程。廢話少說,進入正題。讓咱們先來回顧一下以前的java

Js部分的代碼,以下:api

  

 1 function ItemSelector(elem,opts){
 2     this.elem = elem ;
 3     this.opts = opts ;
 4 } ;
 5 var ISProto = ItemSelector.prototype ;
 6 ISProto.getElem = function(){
 7     return this.elem ;
 8 } ;
 9 ISProto.getOpts = function(){
10     return this.opts ;
11 } ;
12 /* data manip*/
13 ISProto._setCurrent = function(current){
14     this.getOpts()["current"] = current ;
15 } ;
16 ISProto.getCurrentValue = function(current){
17     return this.getOpts()["current"] ;
18 } ;
19 /* data manip*/
20 ISProto.init = function(){
21     var that = this ;
22     this.getOpts()["current"] = null ; // 數據遊標
23     this._setItemValue(this.getOpts()["currentText"]) ;
24     var itemsElem = that.getElem().find(".content .items") ;
25     this.getElem().find(".title div").on("click",function(){
26         itemsElem.toggle() ;
27     }) ;
28     this.getElem().find(".title span").on("click",function(){
29         itemsElem.toggle() ;
30     }) ;
31     $.each(this.getOpts()["items"],function(i,item){
32         item["id"] = (new Date().getTime()).toString() ;
33         that._render(item) ;
34     }) ;
35 } ;
36 ISProto._setItemValue = function(value){
37     this.getElem().find(".title div").text(value)
38 } ;
39 ISProto._render = function(item){
40     var that = this ;
41     var itemElem = $("<div></div>")
42     .text(item["text"])
43     .attr("id",item["id"]) ;
44     if("0" == item["disabled"]){
45         itemElem.on("click",function(){
46             var onChange = that.getOpts()["change"] ;
47             that.getElem().find(".content .items").hide() ;
48             that._setItemValue(item["text"]) ;
49             that._setCurrent(item) ;
50             onChange && onChange(item) ;
51         })
52         .mouseover(function(){
53             $(this).addClass("item-hover") ;
54         })
55         .mouseout(function(){
56             $(this).removeClass("item-hover") ;
57         }) ;
58     }
59     else{
60         itemElem.css("color","#ccc").on("click",function(){
61             that.getElem().find(".content .items").hide() ;
62             that._setItemValue(item["text"]) ;
63         }) ;
64     }
65     itemElem.appendTo(this.getElem().find(".content .items")) ;
66 } ;

  效果以下圖所示:app

      

  a)------非可操做狀態dom

    

  b)------可操做狀態ide

   

(二),打開思路,進行重構學習

  你們從代碼不難看出,已經經過「Js」中的語法特性,以面向對象的方式進行了有效的組織,比鬆散的過程化形式的組織方式好多了,可是仍然會發現有不少不足的地方。測試

  (1),裏面重複代碼太多

  (2),職責劃分不清晰

  (3),流程梳理不健全

  咱們基於以上幾點進行有效的重構,咱們首先要梳理一下這個組件的需求,功能點以下:

  (1),初始化配置組件

    

 1 $(function(){
 2     var itemSelector = new ItemSelector($("#item-selector"),{
 3         currentText : "Please Choose Item" ,
 4         items : [
 5             {
 6                 text : "JavaScript" ,
 7                 value : "js" ,
 8                 disabled : "1"
 9             } ,
10             {
11                 text : "Css" ,
12                 value : "css" ,
13                 disabled : "0"
14             } ,
15             {
16                 text : "Html" ,
17                 value : "html" ,
18                 disabled : "0"
19             }
20         ] ,
21     }) ;
22     itemSelector.init() ;
23 }) ;

  這塊代碼很清晰,不須要作任何修改,可是你們能夠基於以上配置擴展功能,好比增長配置項「mode」支持多種選項方式。如:「checkbox勾選模式」。

 

  接下來是要完成初始化邏輯,以下:

    

 1 ISProto.init = function(){
 2     var that = this ;
 3     this.getOpts()["current"] = null ; // 數據遊標
 4     this._setItemValue(this.getOpts()["currentText"]) ;
 5     var itemsElem = that.getElem().find(".content .items") ;
 6     this.getElem().find(".title div").on("click",function(){
 7         itemsElem.toggle() ;
 8     }) ;
 9     this.getElem().find(".title span").on("click",function(){
10         itemsElem.toggle() ;
11     }) ;
12     $.each(this.getOpts()["items"],function(i,item){
13         item["id"] = (new Date().getTime()).toString() ;
14         that._render(item) ;
15     }) ;
16 } ;

  這段代碼問題不少,職責不明確,初始化邏輯包含了功能點的細節實現。

 

  再繼續看渲染部分代碼:

  

 1 ISProto._render = function(item){
 2     var that = this ;
 3     var itemElem = $("<div></div>")
 4     .text(item["text"])
 5     .attr("id",item["id"]) ;
 6     if("0" == item["disabled"]){
 7         itemElem.on("click",function(){
 8             var onChange = that.getOpts()["change"] ;
 9             that.getElem().find(".content .items").hide() ;
10             that._setItemValue(item["text"]) ;
11             that._setCurrent(item) ;
12             onChange && onChange(item) ;
13         })
14         .mouseover(function(){
15             $(this).addClass("item-hover") ;
16         })
17         .mouseout(function(){
18             $(this).removeClass("item-hover") ;
19         }) ;
20     }
21     else{
22         itemElem.css("color","#ccc").on("click",function(){
23             that.getElem().find(".content .items").hide() ;
24             that._setItemValue(item["text"]) ;
25         }) ;
26     }
27     itemElem.appendTo(this.getElem().find(".content .items")) ;
28 } ;

  問題很明顯,發現了重複性的操做,應該進行合理的抽象,已達到複用的目的。

 

  整個組建的流程包括初始化,渲染(事件綁定),還有就是相關的數據操做方法以及dom操做的輔助方法。

 

  綜上所述,通過簡單的梳理後,咱們應該創建起功能的操做目的以及流程主線的任務分配,各負其責。

  因此咱們重構的目的很明確了,對!就是進行功能點的抽象,友好的職責劃分,那麼咱們如何實現那?

  第一步,創建流程功能方法:(方法接口)

    

ISProto.init = function(){
   // put you code here !
} ;
ISProto._render = function(){
   // put you code here !
} ;

 

 第二部,創建抽象後的方法接口:

  

ISProto._fnItemSelectorDelegateHandler = function(){
   // put you code here !
} ;
ISProto._fnTriggerHandler = function(){
   // put you code here !
} ;
ISProto._addOrRemoveClass = function(){
   // put you code here !
} ;

 

第三步,創建數據操做接口:

  

1 ISProto._setCurrent = function(){
2    // put you code here !
3 } ;
4 ISProto._getCurrent = function(){
5    // put you code here !
6 } ;

  還有一些參照下面的完整源碼,這裏只是說的思路。

 

(三),完整代碼以供學習,本代碼已通過測試

  

function ItemSelector(elem,opts){
	this.elem = elem ;
	this.opts = opts ;
	this.current = -1 ; // 數據遊標
} ;
var ISProto = ItemSelector.prototype ;
/* getter api*/
ISProto.getElem = function(){
	return this.elem ;
} ;
ISProto.getOpts = function(){
	return this.opts ;
} ;
ISProto._getCurrent = function(){
	return this.current ;
} ;
/* getter api*/
/* data manip*/
ISProto._setCurrent = function(current){
	this.current = current ;
} ;
ISProto._setItemText = function(text){
	this.getElem().find(".title div").text(text) ;
} ;
/* data manip*/

/* update on 2015 1/31 23:38 */
ISProto._fnTriggerHandler = function(index,text,value){
	if(this._isDisabled(value)){
		index = -1 ;
		text = this.getOpts()["currentText"] ;
	}
	this._setItemText(text) ;
	this._setCurrent(index) ;
	this.getElem().find(".content .items").hide() ;
} ;
ISProto._addOrRemoveClass = function(elem,className,addIs){
	if(addIs){
		elem.addClass(className) ;
	}
	else{
		elem.removeClass(className) ;
	}
} ;
ISProto._fnItemSelectorDelegateHandler = function(){
	var that = this ;
	this.getElem().on("click","[data-toggle]",function(){
		that.getElem().find(".content .items").toggle() ;
	}) ;
} ;
ISProto._isDisabled = function(value){
	return ("1" == value) ? true : false ;
} ;
/* update on 2015 1/31 23:38 */
ISProto.init = function(){
	var that = this ;
	this._fnItemSelectorDelegateHandler() ;
	$.each(this.getOpts()["items"],function(i,item){
		item["index"] = i ;
		that._render(item) ;
	}) ;
	this._fnTriggerHandler(this._getCurrent(),this.getOpts()["currentText"],"1") ;
} ;
ISProto._render = function(item){
	var that = this ;
	var itemElem = $("<div></div>").text(item["text"]).attr("id",item["index"]) ;
	var activeClass = ("0" == item["disabled"]) ? "item-hover" : "item-disabled-hover" ;
	itemElem.on("click",function(){
		that._fnTriggerHandler(item["index"],item["text"],item["disabled"]) ;
	})
	.mouseover(function(){
		that._addOrRemoveClass($(this),activeClass,true) ;
	})
	.mouseout(function(){
		that._addOrRemoveClass($(this),activeClass,false) ;
	}) ;
	itemElem.appendTo(this.getElem().find(".content .items")) ;
} ;

  

  

(四),最後總結

  (1),面向對象的思考方式合理分析功能需求。

  (2),以類的方式來組織咱們的插件邏輯。

  (3),不斷重構上面的實例,如何進行合理的重構那?不要設計過分,要遊刃有餘,推薦的方式是過程化設計與面向對象思想設計相結合。

    (4),下篇文章中會擴展相關功能,好比「mode」這個屬性,爲"1"時支持checkbox多選模式,如今只是默認下拉模式。

 

                   哈哈哈,本篇結束,未完待續,但願和你們多多交流夠溝通,共同進步。。。。。。呼呼呼……(*^__^*)      

相關文章
相關標籤/搜索