JavaScript插件化开发方式

JavaScript插件化开发方式

  一,开篇分析

  今天这篇文章我们说点什么那?嘿嘿嘿。我们接着上篇文章对不足的地方进行重构,以深入浅出的方式来逐步分析,让大家有一个循序渐进提高的过程。废话少说,进入正题。让我们先来回顾一下之前的

  Js部分的代码,如下:

  复制代码 代码如下:

  function ItemSelector(elem,opts){

  this.elem = elem ;

  this.opts = opts ;

  } ;

  var ISProto = ItemSelector.prototype ;

  ISProto.getElem = function(){

  return this.elem ;

  } ;

  ISProto.getOpts = function(){

  return this.opts ;

  } ;

  /* data manip*/

  ISProto._setCurrent = function(current){

  this.getOpts()["current"] = current ;

  } ;

  ISProto.getCurrentValue = function(current){

  return this.getOpts()["current"] ;

  } ;

  /* data manip*/

  ISProto.init = function(){

  var that = this ;

  this.getOpts()["current"] = null ; // 数据游标

  this._setItemValue(this.getOpts()["currentText"]) ;

  var itemsElem = that.getElem().find(".content .items") ;

  this.getElem().find(".title p").on("click",function(){

  itemsElem.toggle() ;

  }) ;

  this.getElem().find(".title span").on("click",function(){

  itemsElem.toggle() ;

  }) ;

  $.each(this.getOpts()["items"],function(i,item){

  item["id"] = (new Date().getTime()).toString() ;

  that._render(item) ;

  }) ;

  } ;

  ISProto._setItemValue = function(value){

  this.getElem().find(".title p").text(value)

  } ;

  ISProto._render = function(item){

  var that = this ;

  var itemElem = $("

")

  .text(item["text"])

  .attr("id",item["id"]) ;

  if("0" == item["disabled"]){

  itemElem.on("click",function(){

  var onChange = that.getOpts()["change"] ;

  that.getElem().find(".content .items").hide() ;

  that._setItemValue(item["text"]) ;

  that._setCurrent(item) ;

  onChange && onChange(item) ;

  })

  .mouseover(function(){

  $(this).addClass("item-hover") ;

  })

  .mouseout(function(){

  $(this).removeClass("item-hover") ;

  }) ;

  }

  else{

  itemElem.css("color","#ccc").on("click",function(){

  that.getElem().find(".content .items").hide() ;

  that._setItemValue(item["text"]) ;

  }) ;

  }

  itemElem.appendTo(this.getElem().find(".content .items")) ;

  } ;

  效果如下图所示:

  a)------非可操作状态

  b)------可操作状态

  (二),打开思路,进行重构

  大家从代码不难看出,已经通过“Js”中的语法特性,以面向对象的方式进行了有效的组织,比松散的过程化形式的组织方式好多了,但是仍然会发现有很多不足的地方。

  (1),里面重复代码太多

  (2),职责划分不清晰

  (3),流程梳理不健全

  我们基于以上几点进行有效的重构,我们首先要梳理一下这个组件的需求,功能点如下:

  (1),初始化配置组件

  复制代码 代码如下:

  $(function(){

  var itemSelector = new ItemSelector($("#item-selector"),{

  currentText : "Please Choose Item" ,

  items : [

  {

  text : "JavaScript" ,

  value : "js" ,

  disabled : "1"

  } ,

  {

  text : "Css" ,

  value : "css" ,

  disabled : "0"

  } ,

  {

  text : "Html" ,

  value : "html" ,

  disabled : "0"

  }

  ] ,

  }) ;

  itemSelector.init() ;

  }) ;

  这块代码很清晰,不需要做任何修改,但是大家可以基于以上配置扩展功能,比如增加配置项“mode”支持多种选项方式。如:“checkbox勾选模式”。

  接下来是要完成初始化逻辑,如下:

  复制代码 代码如下:

  ISProto.init = function(){

  var that = this ;

  this.getOpts()["current"] = null ; // 数据游标

  this._setItemValue(this.getOpts()["currentText"]) ;

  var itemsElem = that.getElem().find(".content .items") ;

  this.getElem().find(".title p").on("click",function(){

  itemsElem.toggle() ;

  }) ;

  this.getElem().find(".title span").on("click",function(){

  itemsElem.toggle() ;

  }) ;

  $.each(this.getOpts()["items"],function(i,item){

  item["id"] = (new Date().getTime()).toString() ;

  that._render(item) ;

  }) ;

  } ;

  这段代码问题很多,职责不明确,初始化逻辑包含了功能点的细节实现。

  再继续看渲染部分代码:

  复制代码 代码如下:

  ISProto._render = function(item){

  var that = this ;

  var itemElem = $("

")

  .text(item["text"])

  .attr("id",item["id"]) ;

  if("0" == item["disabled"]){

  itemElem.on("click",function(){

  var onChange = that.getOpts()["change"] ;

  that.getElem().find(".content .items").hide() ;

  that._setItemValue(item["text"]) ;

  that._setCurrent(item) ;

  onChange && onChange(item) ;

  })

  .mouseover(function(){

  $(this).addClass("item-hover") ;

  })

  .mouseout(function(){

  $(this).removeClass("item-hover") ;

  }) ;

  }

  else{

  itemElem.css("color","#ccc").on("click",function(){

  that.getElem().find(".content .items").hide() ;

  that._setItemValue(item["text"]) ;

  }) ;

  }

  itemElem.appendTo(this.getElem().find(".content .items")) ;

  } ;

  问题很明显,发现了重复性的操作,应该进行合理的抽象,已达到复用的目的。

  整个组建的流程包括初始化,渲染(事件绑定),还有就是相关的数据操作方法以及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 !

  } ;

  第三步,建立数据操作接口:

  复制代码 代码如下:

  ISProto._setCurrent = function(){

  // put you code here !

  } ;

  ISProto._getCurrent = function(){

  // put you code here !

  } ;

  还有一些参照下面的完整源码,这里只是说的思路。

  (三),完整代码以供学习,本代码已经过测试

  复制代码 代码如下:

  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 p").text(text) ;

  } ;

  /* data manip*/

  /* on 2023 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 ;

  } ;

  /* on 2023 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 = $("

").text(item["text"]).attr("id",item["index"]) ;

  var activeClass = ("0" == item["disabled"]) " : "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多选模式,现在只是默认下拉模式。

  看我本文,是不是要比上一篇代码优秀了很多呢,小伙伴们自己做项目也应该多想多做,尽量使自己的代码更加的合理。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 yyfangchan@163.com (举报时请带上具体的网址) 举报,一经查实,本站将立刻删除