v-model 在 Vue2 和 Vue3 中的區別

v-model 是 Vue 中使用頻率特別高的一個指令,而 Vue3 中的 v-model 有了很大的變化,本文將詳細講述一下 Vue2 和 Vue3 中的 v-model 的區別。javascript

Vue2 中的 v-model

若是對 Vue2 中的語法很熟悉,這部分能夠不看。html

首先來回顧一下 Vue2 中的 v-model,它主要用於表單元素和自定義組件上。v-model本質上是一個語法糖,會對用戶的輸入作一些特殊處理以達到更新數據,而所謂的處理其實就是給使用的元素默認綁定屬性和事件。java

v-model 使用在表單元素上時,會根據元素的不一樣而採用不一樣的處理:api

  • <input type="text">文本<textarea>上使用時,會默認給元素綁定名爲 value 的 prop 和名爲 input 的事件;
  • <input type="checkbox">複選框<input type="radio">單選框 上使用時,會默認綁定名爲 checked 的 prop 和名爲 change 的事件;
  • <select>選擇框 上使用時,則綁定名爲 value 的 prop 和名爲 change 的事件。

這些是 Vue 默認幫咱們處理的,能夠直接使用。可是你也會發現一些第三方組件也能夠使用 v-model ,好比 Element 中的 Input 組件。這是由於這些組件本身實現了 v-model,原理其實就是上面說到的綁定屬性和事件。markdown

咱們能夠嘗試實現一下 v-model,來開發一個簡單的輸入組件,就叫 MyInput 吧:ui

<!-- MyInput 組件代碼 -->

<template>
  <div>
    <input type="text" :value="value" @input="$emit('input',$event.target.value)">
  </div>
</template>

<script> export default { props: { value: String, // 默認接收一個名爲 value 的 prop } } </script>
複製代碼

上面代碼就實現了組件的 v-model 功能,當在這個組件上使用 v-model 時:this

<my-input v-model="msg"></my-input>
複製代碼

其實就等同於:spa

<my-input :value="msg" @input="msg = $event">
複製代碼

Vue 還提供了 model 選項,用於將屬性或事件名稱改成其餘名稱,好比上面的 MyInput 組件,咱們改一下:code

<template>
  <div>
    <input type="text" :value="title" @input="$emit('change', $event.target.value)" />
  </div>
</template>

<script> export default { model: { prop: "title", // 將默認的 prop 名 value 改成 title event: "change", // 將默認的事件名 input 改成 change }, props: { title: String, // 注意 template 代碼中也要修改成 title }, }; </script>
複製代碼

此時使用組件:component

<my-input v-model="msg"></my-input>

// 等同於
<my-input :title="msg" @change="msg = $event"></my-input>
複製代碼

使用 .sync 修飾符

Vue 提供一個 .sync 的修飾符,效果跟 v-model 同樣,也是便於子組件數據更改後自動更新父組件相關數據。實現 .sync 的方式與實現 v-model 殊途同歸,區別就是拋出的事件名須要是 update:myPropName 的結構。

仍是拿上面的 MyInput 說明,咱們仍是傳入一個 title 的 prop,同時組件內部拋出 update:title 事件,代碼以下:

// MyInput 組件中,修改拋出的事件名爲 update:title
 <input type="text" :value="title" @input="$emit('update:title', $event.target.value)" />
複製代碼

此時若是使用這個組件,正常應該是這樣:

<my-input :title="msg" @update:title="msg = $event"></my-input>
複製代碼

但此時能夠使用 .sync 修飾符來簡化:

<my-input :title.sync="msg"></my-input>
複製代碼

能夠看到 .syncv-model 所能達到的效果是同樣的,用什麼就看你什麼場景,通常表單組件上都是用 v-model

Vue3 中的 v-model

上面說了那麼多,爲的就是接下來區別出 Vue3 中 v-model 帶來的變化,主要變化有如下幾處:

修改默認 prop 名和事件名

當用在自定義組件上時,v-model 默認綁定的 prop 名從 value 變爲 modelValue,而事件名也從默認的input 改成 update:modelValue 。在 Vue3 中編寫上面那個 MyInput 組件時,就須要這樣:

<!-- MyInput 組件代碼 Vue3 版 -->

<template>
  <div>
    <input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" // 事件名改成 update:modelValue />
  </div>
</template>

<script> export default { props: { modelValue: String, // 默認 prop 從 value 改成 modelValue }, }; </script>
複製代碼

使用組件時:

<my-input v-model="msg"></my-input>

// 等同於
<my-input :modelValue="msg" @update:modelValue="msg = $event"></my-input>
複製代碼

廢除 model 選項和 .sync 修飾符

Vue3 中移除了 model 選項,這樣就不能夠在組件內修改默認 prop 名了。如今有一種更簡單的方式,就是直接在 v-model 後面傳遞要修改的 prop 名:

// 要修改默認 prop 名,只需在 v-model 後面接上 :propName,例如修改成 title
<my-input v-model:title="msg"></my-input>

// 等同於
<my-input :title="msg" @update:title="msg = $event"></my-input>
複製代碼

注意組件內部也要修改 props:

<template>
  <div>
    <input type="text" :value="title" @input="$emit('update:title', $event.target.value)" />
  </div>
</template>

<script> export default { // 此時這裏不須要 model 選項來修改了 props: { title: String, // 修改成 title,注意 template 中也要修改 }, }; </script>
複製代碼

同時,.sync 修飾符也被移除了,若是你嘗試使用它,會報這樣的錯誤:

'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead

錯誤提示中說明了,能夠使用 v-model:propName 的方式來替代 .sync,由於本質上效果是同樣的。

使用多個 v-model

Vue3 中支持使用多個 v-model,屬於新增功能,我很喜歡這個功能,使得組件數據更新更靈活。例若有這樣一個表單子組件,用戶輸入的多個數據都須要更新到父組件中顯示,能夠這樣寫:

<!-- 表單子組件 Form -->

<template>
  <div class="form">
    
    <label for="name">姓名</label>
    <input id="name" type="text" :value="name" @input="$emit('update:name',$event.target.value)">
    
    <label for="address">地址</label>
    <input id="address" type="text" :value="address" @input="$emit('update:address',$event.target.value)">
  
  </div>
</template>

<script> export default { props:{ name: String, address: String } } </script>
複製代碼

父組件使用這個組件時:

<child-component v-model:name="name" v-model:address="address"></child-component>
    
// 將用戶輸入數據更新到父組件中顯示
<p>{{name}}</p>
<p>{{address}}</p>
複製代碼

自定義 v-model 修飾符

在 Vue2 中的 v-model 上,咱們用過 .trim.lazy.number這三個內置修飾符,而 Vue3 則在這個基礎上增長了自定義修飾符,即開發者能夠自定義修飾符,以按需處理綁定值。

當咱們在 v-model 後面加上自定義修飾符後,會經過名爲 modelModifiers 的 prop 傳遞給子組件,子組件拿到這個修飾符名後,根據條件修改綁定值。咱們來看一個例子,自定義一個修飾符 capitalize,用於將輸入字符串的首字母大寫。

假設自定義組件仍是叫 MyInput,使用 v-model 時加上自定義修飾符 capitalize

<my-input v-model.capitalize="msg"></my-input>
複製代碼

因爲不是內置修飾符,因此須要咱們本身在組件內部處理修飾符邏輯,編寫組件:

<!-- MyInput 組件 -->

<template>
  <div>
    <input type="text" :value="modelValue" @input="emitValue" />
  </div>
</template>

<script> export default { props: { modelValue: String, modelModifiers: { // 自定義修飾符會默認傳入這個 prop 中 type: Object, default: () => ({}), }, }, mounted() { // 當組件 v-model 後面加上了自定義修飾符,組件內部會在 modelModifiers 上獲取到修飾符狀態 console.log(this.modelModifiers); // {capitalize: true} }, methods: { emitValue(e) { let value = e.target.value; // 若是使用了自定義修飾符,即狀態爲 true,就處理值 if (this.modelModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1); } // emit value this.$emit("update:modelValue", value); }, }, }; </script>
複製代碼

這樣就完成了一個將輸入字符串首字母大寫的v-model修飾符。

若是是 v-model 帶上了參數,同時使用了自定義修飾符,好比這樣:

<my-input v-model:title.capitalize="msg"></my-input>
複製代碼

那麼傳入組件內部的 prop 就再也不是 modelModifiers 了,而是 titleModifiers。它的格式是 arg + 'Modifiers'。此時這個組件應該這樣寫:

<!-- MyInput 組件 -->

<template>
  <div>
    <input type="text" :value="title" @input="emitValue" />
  </div>
</template>

<script> export default { props: { title: String, // modelValue -> title titleModifiers: { // modelModifiers -> titleModifiers type: Object, default: () => ({}), }, }, mounted() { console.log(this.titleModifiers); // {capitalize: true} }, methods: { emitValue(e) { let value = e.target.value; // 若是使用了自定義修飾符,就處理值 if (this.titleModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1); } // emit value this.$emit("update:title", value); }, }, }; </script>
複製代碼

結語

以上就是 v-model 在 Vue2 和 Vue3 中的區別表現,我我的以爲新版中使用邏輯更清晰了。只是有些部分再也不兼容了,若是是直接將 Vue2 版本代碼移植到 Vue3 項目中,須要注意 v-model 的實現差別,還有注意要將 .sync 修飾符替換成新寫法。

文中若有不當之處,歡迎評論區指出。

相關文章
相關標籤/搜索