使用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来重写一遍密码框,最大的感触就是“麻雀虽小,五脏俱全”,实现一个小小的组件原来要考虑的东西还真多。