一,开篇分析
今天这篇文章我们说点什么那?嘿嘿嘿。我们接着上篇文章对不足的地方进行重构,以深入浅出的方式来逐步分析,让大家有一个循序渐进提高的过程。废话少说,进入正题。让我们先来回顾一下之前的
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 div").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 div").text(value) } ; ISProto._render = function(item){ var that = this ; var itemElem = $("<div></div>") .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 div").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 = $("<div></div>") .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 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多选模式,现在只是默认下拉模式。
看我本文,是不是要比上一篇代码优秀了很多呢,小伙伴们自己做项目也应该多想多做,尽量使自己的代码更加的合理。
本文向大家介绍JavaScript插件化开发教程(五),包括了JavaScript插件化开发教程(五)的使用技巧和注意事项,需要的朋友参考一下 一,开篇分析 Hi,大家好!前两篇文章我们主要讲述了以“jQuery的方式如何开发插件”,以及过程化设计与面向对象思想设计相结合的方式是如何设计一个插件的,两种方式各有利弊取长补短,嘿嘿嘿,废话少说,进入正题。直接上实际效果图: 大家看到了吧,这是一个
这是一本讲述kibana插件开发教程类的书籍,写这本书的时候,还没有深入开发过kibana插件,书中难免会出现各种错误,欢迎大家指正交流。
本文向大家介绍jQuery插件开发详细教程,包括了jQuery插件开发详细教程的使用技巧和注意事项,需要的朋友参考一下 扩展jQuery插件和方法的作用是非常强大的,它可以节省大量开发时间。这篇文章将概述jQuery插件开发的基本知识,最佳做法和常见的陷阱。 一、入门 编写一个jQuery插件开始于给jQuery.fn加入新的功能属性,此处添加的对象属性的名称就是你插件的名称: 用户非常喜欢的
重要说明 IntelliJ IDEA 的插件理论上是同时也适用于 JetBrains 公司下的其他大多数 IDE 的,因为这些 IDE 都是基于 IntelliJ IDEA 的基础平台进行开发的,请牢记这一点。 教程视频下载和介绍 视频章节结构: 01_AS插件是什么_和IntellijIDEA关系 02_常见的AS开发插件 03_创建第一个Plugin插件 04_翻译插件需求 05_翻译开发过程
确定功能,先给插件起名,英文名和中文名 如:Demo,插件演示,然后在public/plugins目录里添加这个插件目录(demo) 确定是否要后台配置文件,如果需要在插件根目录加上config.php,格式可看文档“插件配置文件” 创建插件主类文件,查看“插件类主文件” 确定是否要模板,如需要请根目录添加view目录 确定是否可以外部访问,如需要请加controller目录,再添加Control
确定功能,先给插件起名,英文名和中文名 如:Demo,插件演示,然后在plugins目录里添加这个插件目录 确定是否要后台配置文件,如果需要在插件根目录加上config.php,格式可看文档“插件配置文件” 创建插件主类文件,查看“插件类主文件” 确定是否要模板,如需要请根目录添加View目录 确定是否可以外部访问,如需要请加Controller目录,再添加Controller文件 给自己的模板(