從零實現Vue的元件庫(八)- Input 實現

FatGe發表於2019-01-01

基於原生的 HTML 標籤進行擴充的 Input 元件。

Input 元件一般是一個元件庫的基礎,很多元件都需要依賴它,所以該元件的特點在於:
  • 支援原生的功能,結合 this.$slots 擴充 slot,便於二次封裝;
  • v-modelv-on=$listeners的相關處理;
  • 樣式的封裝以及抽離。

1. 例項

最終效果

程式碼

<!-- 基礎用法 -->
<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 UI-Library

2. 原理

首先擴充 type 屬性,新增值為 textarea 的狀態,設計該元件的結構為 input 如下

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

    <template v-else>
        <input
            :class="['input-inner']"
            :type="type"
        />
    </template>
</div>
複製程式碼

基礎功能實現:

  • 新增 v-bind="$attrs" 指令,來實現原生 input 的相關屬性,例如 placeholderreadonlymaxlength等,在 input 以及 textarea 上,該指令能夠在元件上繫結父作用域中不作為 prop 被識別 (且獲取) 的特性 (class 和 style 除外);

  • 新增 v-on="$listeners"指令,實現元件上的 changefocusblur 事件,由於 Input 元件需要支援 v-model="inputValue" ,而在 input標籤 上 v-model 依賴 input 事件, 其原理是

    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 value

    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>
複製程式碼

往期文章:

原創宣告: 該文章為原創文章,轉載請註明出處。

相關文章