使用Vue实现一个UI组件的小结


世间最痛苦的莫过于当你飞奔到厕所拉屎,结果忘记带纸...

如果你觉得这是一篇扯淡的文章,那我要郑重地告诉你,you are wrong!这是一篇扯Vuejs的文章!

近两年来,angularjs方兴未艾之际,MVVM的前端框架又出新贵,react在Facebook这位富一代爸爸的推广下如雨后春笋在各大社区、团队、产品中大量应用。网上、书本上关于这些MV*框架的介绍、对比也已经非常多。本文想结合实际项目中的一个案例聊聊后起之秀Vuejs的一些使用心得,希望不致污染各位读者的眼睛。

先介绍一下背景:项目中经常会遇到密码输入的需求。要求支持用户输入数字密码,可自定义密码位数,可自定义支持输入完自动检测输入数值或按钮确认时检测输入数值、可设置密码输入框的标题。

先看看页面的展示,如图1:


图1

如果我们对该UI组件进行切割,可以大致将整个UI组件分成3个部分:

  • 组件的title(顶部文字说明)
  • 密码输入框
  • 按钮

其中“组件title”、“按钮”其实对于核心功能而言不是必须的,把组件的核心功能抽象到最简单的话就是支持输入几个特定类型的字符

整体的结构如图2所示:


图2

因此,当我们封装完整个密码框组件以后,根据高内聚、低耦合的原则,暴露给调用者的几个接口也就显而易见。如下列代码

require(['../js/module/hna-passwordDialog.js'],function (app) {        
        app.init('#firstPassword',{        
                title : '请确认支付密码',        
                hasButton : true,        
                completeCallback : function (code) {            
                        //输入达标后的回调函数            
                        console.log(code);        
                },        
                errorCallback : function () {            
                        //输入的值非法时的回调函数            
                        console.log(error);        
                }    
        });
});

标题的内容、是否有按钮、输入内容校验成功的处理器、输入内容校验失败的处理器是整个组件使用者需要关注的。

接下来,我们考虑整个UI组件的交互逻辑。

组件初始化阶段

组件初始化

用户交互阶段

用户交互

前面啰啰嗦嗦地大谈整个组件的设计思路,但貌似跟我们标题中的主角(Vue)八竿子都打不到,其实不然,正是有了这些对于组件设计和实现的流程、结构的思考,那么在使用特定工具(Vue)时才能事半功倍,下面就让我们来一起聊聊如何使用Vue来搞定整个UI组件的渲染和事务处理。

组件模板
模板代码如下:

'<div class="hna-passwordDialog">\                
        <p class="hna-password-title">{{title}}</p>\                
        <div class="password-view-wrapper">\                    
                <div class="password-view">\                        
                        <div class="password-item" v-bind:class="{dot : codeArr[i-1]}" v-for="i in count" v-bind:style="{width:100/count + \'%\'}"></div>\                    
                </div>\                    
                <label class="input-trigger"><input type="tel" v-bind:maxlength="count" v-bind:value="code" v-on:input="chargeInput"></label>\                
        </div>\                
        <div class="btn-wrapper" v-if="hasbutton">\                    
                 <button class="hna-button btn-large btn-strong" v-bind:class="{dis : !isActive} " v-on:click.prevent="submit">确定</button>\                
        </div>\            
</div>'

模板是Vue一个常用的功能,将组件中可用于用户设置的部分均通过变量(动态值)的形式来动态创建,上述代码中将组件的标题、输入框的个数、确认按钮等的展示跟相应的数据关联起来。

父子组件数据传递
组件的一些数据是需要从外部进行设置的,这些是暴露给用户的对外接口,这里使用了Vue的父组件向子组件传递数据的方式。通过props的形式,同时还可以在初始化的时候设置相应属性值的类型和默认值,代码如下:

<hna-password-dialog v-bind:count="count" v-bind:title="title" v-bind:hasbutton="hasbutton" v-on:complete="completeCallback" v-on:error="errorCallback"></hna-password-dialog>
props : {    
    //标题    
    title : {        
        type : String,        
        default : '请输入信息'    
    },    
    //多少个输入的格子    
    count : {        
        type : Number,        
        default : 6    
    },    
    //是否需要确认按钮    
    hasbutton : {        
        type : Boolean,        
        default : true    
    }
}

计算属性
组件的渲染需要的一些数值并不能直接使用初始传入的值,而是根据特定算法或逻辑计算后的数值,这个时候就需要用到Vue的计算属性。密码框组件中我把每个输入框的展示对应到一个数组上,如果数组的值为空则不添加dot类名,否则添加dot类名,但是这个数组是要根据实际输入的密码串计算而来的。代码如下:

computed :{    
   codeArr : function () {        
      var ret = [];        
      var count = this.count;        
      for(var i = 0 ; i < count ; i++){            
         ret[i] = this.code[i];        
      }        
      return ret;    
   }
}

组件独立数据
组件在定义的时候是针对一类具有统一行为的UI表现和用户行为逻辑的封装,但是具体每个组件在相应的场景中是有自己独立的数据集合的,以此为依据来渲染自身。密码输入框组件最为关键的数据是用户实际输入的密码串,而且这个数据是不能被所有组件公用,因此它不能直接通过data对象设置,而需要通过闭包函数保存在每个独立的组件内部。代码如下:

data : function () {    
   return {        
      code : '',        
      //按钮是否可点击        
      isActive : false 
   }
}

自定义事件
密码框组件在用户完成输入后是会对输入的密码串进行校验,并且会返回校验成功会失败的结果,为了让组件的配置性更强,同时便于组件的使用者能够更加专注于业务逻辑的开发,暴露给用户2个事件并提供设置回调函数的入口是个非常好的解耦策略。
结构如下图

父子组件事件通信

代码如下:
首先在html上注册相应的事件监听

<hna-password-dialog v-bind:count="count" v-bind:title="title" v-bind:hasbutton="hasbutton" v-on:complete="completeCallback" v-on:error="errorCallback"></hna-password-dialog>

然后在组件逻辑中进行相应的事件触发

methods : {    
   chargeInput : function (e) {        
   var _this = e.target || e.srcElement;       
   //判断输入的值是否整数        
   this.code = _this.value;        
   if(this.code.length >= this.count){            
      this.isActive = true;            
      if(!this.hasbutton){                
         this.validCode(this.code);            
      }        
   }else{            
      this.isActive = false;        
   }    
},    
submit : function (e) {        
   var _this = e.target || e.srcElement;        
   var code = this.code;        
   //按钮不可点击的状态直接不操作        
   if(_this.classList.contains('dis')){            
      return;        
   }        
   //判断是否输出符合长度的密码、验证码        
   if(code.length < this.count){            
      console.log('请输入符合长度的数值');            
      return;        
   }        
   //校验code        
   this.validCode(code);    
},    
validCode : function (code) {        
   //验证输入的数值是否正确        
   if(/^\d+$/g.test(code)){            
      this.$emit('complete',code);        
   }else{            
      this.$emit('error',code);        
   }    
}

最后通过Vue对象来进行组件内部事件和外部处理的桥接

methods : {    
   //输入合法的值的回调    
   completeCallback : function (code) {        
      if(param.completeCallback){            
         param.completeCallback(code);        
      }    
   },    
   //输入非法的值的回调    
   errorCallback : function () {        
      if(param.errorCallback){            
         param.errorCallback();        
      }    
   }
}

类名和内联样式的绑定
先看看代码

<div class="password-item" v-bind:class="{dot : codeArr[i-1]}" v-for="i in count" v-bind:style="{width:100/count + \'%\'}"></div>\

这是一个根据用户设置的输入框数目来动态生成的输入框列表,这一段代码里面涉及到类名的动态绑定、内敛样式的动态绑定与计算,通过控制相应的数据来动态决定组件各部分的渲染。

到此,基本上整个UI组件的设计、实现就告一段落啦。

后语:
在几个月前,我使用原生的js实现了一个密码框UI组件,但总是觉得代码很啰嗦,当然也很有可能是自己的水平不够。这次因为整个项目中引入vue作为UI渲染的库,所以就尝试着使用vue来重写一遍密码框,最大的感触就是“麻雀虽小,五脏俱全”,实现一个小小的组件原来要考虑的东西还真多。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容