作者:孫輝,美團金融前端團隊成員。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,分別為 prop
、event
,值分別為 checked
、change
,看到這裡,我們彎起嘴角,會心一笑。
model
屬性值(model這個名稱真是取得簡明扼要啊)裡的兩個key其實就是 v-model
這個語法糖所代表的 prop 和 event,分別表示 該表單元素的值 和 改變元素值時觸發的事件, 在 input 中,這兩個值是value
和 input
(預設值),在 checkbox 中表示 checked
和 change
。
以此類推,在 select 中就表示 selected
和 change
。
到這裡,我就需要指出我們上面說的一個錯誤了,此時的 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
最後,團隊為了招聘方便,整了個公眾號,主要是一些招聘資訊,團隊資訊,所有的技術文章在公眾號裡也可以看到,對了,如果你想去美團其他團隊,我們也可以幫你內推哦 ~