Vue造轮子系列-Input组件

1,625 阅读4分钟

1、前言

今天写的这个组件是平时很常用的一个,输入框(Input)组件,常用于各种表单中。那么话不多说,直接开始造轮子吧。

2、Input组件-需求

首先造一个组件,得需要知道它的需求是吧,不然需求不清楚,在写的过程中就会很痛苦,我在写输入框的时候,主要考虑了以下这几个需求:

  • 输入状态(包括提示,报错)
  • 不可输入disabled
  • 只读状态readonly
  • 支持输入框中自定义图标
  • icon图标的位置
  • 输入时的事件

以上就是我在做Input组件时想到的几个点,其它一些复杂的功能就没考虑啦。

3、Input组件-实现

明确需求之后,那么就要想一下用户使用组件的API了,大概就是如下进行调用:

<y-input value="hello world"></y-input>
<y-input v-model="value" readonly></y-input>
<y-input v-model="value" disabled></y-input>
<y-input value="hello world" error="输入格式不对"></y-input>
<y-input value="" icon="settings" placeholder="enter something"></y-input>

上面主要列了几个,其它的就看下面的属性表格,往里面加就好啦。

知道用户怎么使用按钮之后,接下来就是代码实现了。Input组件主要有几个要考虑的地方,

  • 错误信息的提示
  • 图标的位置的实现,左边(默认)和右边
  • 事件的处理

接下去我会仔细讲解组件的代码,代码如下:

<div class="y-input" :class="{ error }">
    <y-icon v-if="icon !== ''" :name="icon" :class="classes"></y-icon>
    <input
        :value="value"
        :type="type"
        :disabled="disabled"
        :readonly="readonly"
        :placeholder="placeholder"
        :class="{ [`inline-icon-${iconPosition}`]: icon !== '' }"
        @change="$emit('change', $event.target.value)"
        @focus="$emit('focus', $event.target.value)"
        @blur="$emit('blur', $event.target.value)"
        @input="$emit('input', $event.target.value)"
    />
    <template v-if="error">
        <y-icon name="error" class="icon-error"></y-icon>
        <span class="message-error">{{ error }}</span>
    </template>
</div>

接收的参数如下:

props: {
    icon: {
      type: String,
      default: '',
    },
    iconPosition: {
      type: String,
      default: 'left',
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    value: {
      type: String,
    },
    type: {
      type: String,
      default: 'text',
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    error: {
      type: String,
    },
    placeholder: {
      type: String,
      default: '',
    },
}

具体含义如下:

参数说明类型可选值默认值
value绑定值string
type类型stringtext,textarea 和其他原生 input 的 type 值text
readonly原生属性,是否只读booleanfalse
error错误信息string
disabled禁用booleanfalse
placeholder输入框占位文本string
icon图标名称string
iconPosition图标位置stringleft / rightleft

上面就是组件的主要代码了,然后各个属性的说明也在表格中给出,接下去主要就是说明一下实现的主要思路,

  1. 首先给最外面的div添加一个动态类error,这个主要用来控制有错误信息的时候,显示错误提示,没有error那就显示正常的输入框。

  2. 第二行的<y-icon>就是用来控制图标的显示,如果用户传了icon参数,那么就显示图标,否则就不显示,注意这里的动态class,主要是用来控制图标是显示在左侧还是右侧,

computed: {
    classes() {
        return {
          [`icon-left`]: this.icon !== '' && this.iconPosition === 'left',
          [`icon-right`]: this.icon !== '' && this.iconPosition === 'right',
        }
    },
},

对于图标的控制,主要还是通过定位来实现,让其相对于输入框进行定位,然后对输入框相应的设置一个padding,部分css样式如下:

&.inline-icon-left {
    padding-left: 2em;
}
&.inline-icon-right {
    padding-right: 2em;
}
.icon-left {
    position: absolute;
    top: 50%;
    left: 10px;
    transform: translateY(-50%);
    z-index: 2;
}
.icon-right {
    position: absolute;
    top: 50%;
    right: 6px;
    transform: translateY(-50%);
    z-index: 2;
}
  1. 第三行开始的就是真正的input组件部分了,首先它接受一个value,用来表示输入框的值,支持双向绑定。然后type的话表示支持的输入框类型,可以详见原生 input 的 type 值

    同时,输入框支持的事件有,changefocusblurinput,在这里我没有对这些事件做相应的处理,只是对其进行了向外触发,并将输入框的值传递给使用这个组件的父组件,让用户得到这个值根据自己的需求去处理。

    当时也有想过这么处理事件,就是用户通过传递回调函数,在组件中去执行,然后把处理后的再传出去。但是这样子设计显然更麻烦,而且使用这个组件的体验也不好,所以放弃了这种想法。

  2. 最后的template主要是用于控制显示错误信息,这里的template其实就是一个占位符的作用,不会真正显示在文档中,这样子比用div可以节省DOM元素。然后里面的内容就是一个错误的icon和具体的错误信息。这个主要用于表单验证的时候,当验证失败,这个错误信息就能用上啦。

  3. 最后一步,就是给各个状态添加样式了。

到此,Input组件就已经造完了,是不是很简单哈!

4、Input组件-遇到的问题

这个组件和昨天的Button组件其实都是利用了原生的html元素,然后在其基础进行改写,添加了不同状态,不同场景下的样式,所以没有什么难度。

5、结束

今天的组件还是很简单吧,主要样式写的好,你也可以做出一个好看的Input组件。下一个组件打算写一下栅格组件,那个就没有这两个这么简单了,加油!

项目地址Yue UI