v-model指令在元件中怎麼玩

前端深夜告解室發表於2019-03-04

作者:孫輝,美團金融前端團隊成員。15年畢業加入美團,相信技術,更相信技術只是大千世界裡知識的一種,個人部落格: sunyuhui.com

備註:文章內容和案例均基於Vue2(具體版本為Vue2.3.4)

筆者最近在寫元件的時候,遇到了 v-model 的使用問題,在 Vue 官方文件中,有兩小端內容是關於 v-model 指令在元件中的使用,查閱文件後,依然不得要領,最後幾番折騰,理論結合實踐,終於領悟其精髓,遂成文分享之。

v-model 通常都是運用在表單元件中,在這裡我們以一個 select元件為例,元件命名為 a-select

v-model寫在哪?

第一個問題就是 v-model 指令是寫在子元件裡還是父元件裡。

我在最開始寫元件時就遇到這個問題,歸根結底是對在元件中使用 v-model 指令的瞭解還處於混沌的狀態。

文件中有提到

要讓元件的 v-model 生效,它應該 (在 2.2.0+ 這是可配置的):

  • 接受一個 value 屬性
  • 在有新的值時觸發 input 事件

所以我們需要通過觸發事件來實現 value 的更新,而 Vue 中:

父子元件的關係可以總結為 props down, events up

那麼很明顯,我們是在父元件裡寫 v-model

子元件怎麼更新父元件的值?

那在父元件中我們可以這麼寫:

    <a-select v-model="parentValue" ></a-select>複製程式碼

文件 告訴我們,v-model 只是一個語法糖,實際的含義是:

    <a-select
      v-bind:value="parentValue"
      v-on:input="parentValue = arguments[0]">
    </a-select>複製程式碼

那在子元件中,怎麼更新父元件的值(parentValue)呢?我翻遍了文件,也沒找到,但我找到了一段看似相關的 定製元件的 v-model,因為其中說了:

預設情況下,一個元件的 v-model 會使用 value 屬性和 input 事件,但是諸如單選框、核取方塊之類的輸入型別可能把 value 屬性用作了別的目的

同樣,我們的 select 元件的 value 值也被佔用,而且沒有 input事件

看來我們需要定製 v-model 了, 開始之前,我們先來把例子看懂。

   Vue.component(`my-checkbox`, {
      model: {
        prop: `checked`,
        event: `change`
      },
      props: {
        checked: Boolean,
        // this allows using the `value` prop for a different purpose
        value: String
      },
      // ...
    })複製程式碼

新增的 model 屬性值裡有兩個key,分別為 propevent,值分別為 checkedchange,看到這裡,我們彎起嘴角,會心一笑。

model 屬性值(model這個名稱真是取得簡明扼要啊)裡的兩個key其實就是 v-model 這個語法糖所代表的 prop 和 event,分別表示 該表單元素的值改變元素值時觸發的事件, 在 input 中,這兩個值是valueinput(預設值),在 checkbox 中表示 checkedchange

以此類推,在 select 中就表示 selectedchange

到這裡,我就需要指出我們上面說的一個錯誤了,此時的 v-model 在父元件中的實際含義是:

    <a-select
      v-bind:selected="parentValue"
      v-on:change="parentValue = arguments[0]">
    </a-select>複製程式碼

那麼我們可以這麼來寫子元件:

    <select @change="emitChange($event.target.value)">
        <template v-for="item in selectData">
            <option :value="item.value">{{item.text}}</option>
        </template>
    </select>

    export default() {
        model: {
            prop: `selected`,
            event: `change`
        },
        props: {
            selectData: {
                type: Array
            },
        },
        methods: {
            emitChange(value){
                this.$emit(`change`, value);
            }
        }
    }複製程式碼

唯一的問題在於,我們需要在初始化時設定選中項,該怎麼辦?我們還有一個 selected 屬性值沒有呢。甚至官網也溫馨提示我們:

注意你仍然需要顯性宣告 checked 屬性。

所以這裡我們需要顯性宣告 selected 屬性,不過,因為有 v-model 的存在,我們可以不用在父元件裡傳入 selected值,是不是少了一點工作量呢?

所以子元件裡是這麼寫的:


    <select :value="selected" @change="emitChange($event.target.value)">
        <template v-for="item in selectData">
            <option :value="item.value">{{item.text}}</option>
        </template>
    </select>

    export default() {
        model: {
            prop: `selected`,
            event: `change`
        },
        props: {
            selectData: {
                type: Array
            },
            selected: {
                type: [String,Number]
            }
        },
        methods: {
            emitChange(value){
                this.$emit(`change`, value);
            }
        }
    }複製程式碼

當然了,作為一個完整的 select 元件,上面的示例其實是很簡陋的。

完整的 select 元件程式碼可以看這裡 , Demo 可以看這裡

done

最後,團隊為了招聘方便,整了個公眾號,主要是一些招聘資訊,團隊資訊,所有的技術文章在公眾號裡也可以看到,對了,如果你想去美團其他團隊,我們也可以幫你內推哦 ~

二維碼
二維碼

相關文章