Input 实现

基于原生的 HTML 标签进行拓展的 Input 组件。html

Input 组件通常是一个组件库的基础,不少组件都须要依赖它,因此该组件的特色在于:
  • 支持原生的功能,结合 this.$slots 拓展 slot,便于二次封装;
  • v-modelv-on=$listeners的相关处理;
  • 样式的封装以及抽离。

1. 实例

最终效果

代码git

<!-- 基础用法 -->
<fat-input placeholder="请输入内容" v-model="inputValue" />

<!-- 复合型输入框 -->
<fat-input placeholder="请输入内容">
    <template slot="prepend">
        <div class="prepend-part c-size-s">Http://</div>
    </template>

    <template slot="append">
        <div class="append-part c-size-s">.com</div>
    </template>
</fat-input>
复制代码

实例地址:Input 实例github

代码地址:Github UI-Librarybash

2. 原理

首先拓展 type 属性,添加值为 textarea 的状态,设计该组件的结构为 input 以下app

<div :class="['input-wrapper']">
    <textarea
        v-if="type === 'textarea'"
        class="textarea-inner"
    />

    <template v-else>
        <input
            :class="['input-inner']"
            :type="type"
        />
    </template>
</div>
复制代码

基础功能实现:ui

  • 添加 v-bind="$attrs" 指令,来实现原生 input 的相关属性,例如 placeholderreadonlymaxlength等,在 input 以及 textarea 上,该指令可以在组件上绑定父做用域中不做为 prop 被识别 (且获取) 的特性 (class 和 style 除外);this

  • 添加 v-on="$listeners"指令,实现组件上的 changefocusblur 事件,因为 Input 组件须要支持 v-model="inputValue" ,而在 input标签 上 v-model 依赖 input 事件, 其原理是spa

    event => this.$emit("input", event.target.value)
    复制代码

    可是直接使用时,$emit("input", arg)arg 是一个 [object InputEvent],与实际应用状况不服, 二者会报异常,这时利用 computed 属性对 $listeners 进行修改设计

    export default {
        model: {
            prop: "value",
            event: "input"
        },
        computed: {
            inputListeners() {
                return Object.assign({}, this.$listeners, {
                    input: event => this.$emit("input", event.target.value)
                });
            }
        }
    };
    复制代码

    而后再利用 v-on="inputListeners" 绑定到组件上,同时 watch valuecode

    watch: {
        value: {
            handler(newVal) {
                this.inputValue = newVal;
            },
            // 添加immediate,减小created生命周期中的赋值
            immediate: true
        }
    }
    复制代码

样式拓展实现:

  • 添加icon(非 textarea 时生效),添加
    <fat-icon v-if="prefixIcon" class="icon" :name="prefixIcon"/>
    
    <input
        :class="['input-inner']"
        :type="type"
        :value="inputValue"
        v-bind="$attrs"
        v-on="inputListeners"
    />
    
    <fat-icon v-if="suffixIcon" class="icon" :name="suffixIcon"/>
    复制代码
  • 实现复合型输入框时,主要依靠具名插槽
    <slot name="prepend"></slot>
    
    <input
        :class="['input-inner', { 'have-prepand': havePrepand, 'have-append': haveAppend }]"
        :type="type"
        :value="inputValue"
        v-bind="$attrs"
        v-on="inputListeners"
    />
    
    <slot name="append"></slot>
    复制代码
    而且为了兼容样式,须要对 input-inner 添加两个类,have-prepand 以及 have-append,它们的状态主要是检查 this.$slots 对象
    computed: {
        havePrepand() {
            return this.$slots.prepend;
        },
        haveAppend() {
            return this.$slots.append;
        }
    }
    复制代码

3. 使用

因为上述实现过程,都是在原生的 Input 组件进行的拓展,在实际应用时,可结合业务进行封装以及抽象。 使用时,主要注意点两个具名插槽的使用。

<div class="demo-row-content">
    <fat-input placeholder="请输入内容"> <template slot="prepend"> <div class="prepend-part c-size-s">Http://</div> </template> <template slot="append"> <div class="append-part c-size-s">.com</div> </template> </fat-input> </div>
复制代码

原创声明: 该文章为原创文章,转载请注明出处。

相关文章
相关标签/搜索