根據上一篇《深入理解 v-model 之表單用法》基本對 v-model 有了比較深的理解,接下來我們看看它如何在自定義組件中使用。
首先,我們知道下面兩個用法等價的:
<input?v-model="msg"?/>
<input?:value="msg"?@input="msg?=?$event.target.value"?/>
在 vue3 中
當在自定義組件中使用v-model
時,組件接收一個屬性modelValue
的值,然后通過觸發update:modelValue
事件來更新該值:
<custom-comp?v-model="msg">custom-comp>
<custom-comp?:model-value="msg"?@update:model-value="msg?=?$event">custom-comp>
v-model 實現
根據上面的定義規則,我們可以這樣實現一個自定義 input 組件:
//?示例1:自定義input組件
//?實現1:
app.component('custom-input',?{
??props:?['modelValue'],
??template:?`??????:value="modelValue"
??????@input="$emit('update:modelValue',?$event.target.value)"
????>
??`,
});
//?實現2:使用input的v-model + computed(計算屬性)
app.component('custom-input',?{
??props:?['modelValue'],
??computed:?{
????value:?{
??????get()?{
????????return?this.modelValue;
??????},
??????set(v)?{
????????this.$emit('update:modelValue',?v);
??????},
????},
??},
??template:?`
??`,
});
使用:
<custom-input?v-model="msg">custom-input>;
上面示例只是對 input 做了一層包裝,如果自定義組件里面不包含 input 又該如何實現呢?為了加深理解,我們看下面一個自定義 count 組件示例:
//?示例2:自定義count組件
app.component('custom-count',?{
??props:?{
????modelValue:?Number,
??},
??methods:?{
????increment()?{
??????this.$emit('update:modelValue',?++this.modelValue);
????},
????decrement()?{
??????this.$emit('update:modelValue',?--this.modelValue);
????},
??},
??template:?`+1?~?-1
{{modelValue}}
??`,
});
使用:
<custom-count?v-model="num">custom-count>;
我們來看看實現①:
v-model 參數
通過示例我們發現 v-model 是接收屬性modelValue
的值,然后觸發事件update:modelValue
來更新該值,那么我們可不可以修改這個屬性名modelValue
呢?該如何操作?其實我們只需要給v-model
添加參數即可,比如:v-model:mv
,這樣就將modelValue
換成了mv
。
我們來將上面的自定義組件改造一下:
app.component('custom-input',?{
??props:?['mv'],
??template:?`??????:value="mv"
??????@input="$emit('update:mv',?$event.target.value)"
????>
??`,
});
使用方式就變成了:
<custom-count?v-model:mv="num">custom-count>;
多個 v-model 綁定
正是由于 vue3 中新增了 v-model 的參數傳遞,所以自定義組件可以同時支持多個v-model
的綁定:
<user-name?v-model:first-name="firstName"?v-model:last-name="lastName">user-name>
組件實現就變成了:
app.component('user-name',?{
??props:?{
????firstName:?String,
????lastName:?String,
??},
??template:?`??????type="text"
??????:value="firstName"
??????@input="$emit('update:firstName',?$event.target.value)">??????type="text"
??????:value="lastName"
??????@input="$emit('update:lastName',?$event.target.value)">
??`,
});
實現效果②:
在 vue2 中
當在自定義組件中使用v-model
時,組件接收一個屬性value
的值,然后通過觸發input
事件來更新該值:
<custom-comp?v-model="msg">custom-comp>
<custom-comp?:value="msg"?@input="msg?=?$event">custom-comp>
v-model 實現
實現方式類似,我們看下 vue2 中實現一個自定義 input 組件:
//?示例1:自定義input組件
Vue.component('comp-input',?{
??props:?{
????value:?String,
??},
??template:?`??????type="text"
??????:value="value"
??????@input="$emit('input',?$event.target.value)"
????>
??`,
});
自定義 v-model 屬性
同樣在 vue2 中也支持修改接收的屬性名,只是和 vue3 不同,vue2 是通過在組件中指定屬性 model
的 prop
和 event
來修改:
//?示例2:自定義count組件
Vue.component('custom-count',?{
??model:?{
????prop:?'v',?//?default:?value
????event:?'i',?//?default:?input
??},
??props:?{
????v:?Number,
??},
??data()?{
????return?{
??????count:?this.v,
????};
??},
??template:?`+1`,
});
我們看到在這個示例里面多了一個model屬性
,并指定了兩個屬性:prop
和event
,沒錯,這正是 v-model 需要的屬性和事件名,只是他們的默認值為value
和input
,我們通過修改 model 屬性的 prop 和 event 就實現了自定義。
在線效果③:
關于為什么要出來一個 model 屬性,官方文檔④也有說明,就是為了避免和 value 值有其他用途時和 v-model 產生沖突,比如單選框、復選框,具體可以查看官方示例④。
總結
自定義組件的 v-model 我們通過在 vue3 和 vue2 中的實現都講解了一遍,而且也能發現了其中的差異:
- vue3 默認屬性名、事件名為:
modelValue
和update:modelValue
;而 vue2 中則是:value
和input
; - vue3 中直接通過 v-model 后面參數
v-model:foo
來指定屬性名,而且修改體現在父組件中,并且支持綁定多個 v-model
;而 vue2 中通過子組件的model 屬性中的prop值和event值
來指定屬性名和事件名,修改體現在子組件中。
接下來我們來看下一篇:《深入理解 vue 中 v-model 之修飾符》。
文章引用鏈接:
- https://codepen.io/cleam_lee/pen/ExKMYKE
- https://codepen.io/cleam_lee/pen/MWyRpvg
- https://codepen.io/cleam_lee/pen/mdPvWvY
- https://cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model