v-model
可以在組件上使用以實現雙向綁定。
首先讓我們回憶一下 v-model
在原生元素上的用法:
<input v-model="searchText" />
在代碼背後,模板編譯器會對 v-model
進行更冗長的等價展開。因此上面的代碼其實等價於下面這段:
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
而當使用在一個組件上時,v-model
會被展開為如下的形式:
<CustomInput
:model-value="searchText"
@update:model-value="newValue => searchText = newValue"
/>
要讓這個例子實際工作起來,<CustomInput>
組件內部需要做兩件事:
<input>
元素的 value
attribute 綁定到 modelValue
propinput
事件觸發時,觸發一個攜帶了新值的 update:modelValue
自定義事件這里是相應的代碼:
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
現在 v-model
可以在這個組件上正常工作了:
<CustomInput v-model="searchText" />
另一種在組件內實現 v-model
的方式是使用一個可寫的,同時具有 getter 和 setter 的 computed
屬性。get
方法需返回 modelValue
prop,而 set
方法需觸發相應的事件:
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>
<template>
<input v-model="value" />
</template>
v-model
的參數默認情況下,v-model
在組件上都是使用 modelValue
作為 prop,並以 update:modelValue
作為對應的事件。我們可以通過給 v-model
指定一個參數來更改這些名字:
<MyComponent v-model:title="bookTitle" />
在這個例子中,子組件應聲明一個 title
prop,並通過觸發 update:title
事件更新父組件值:
<!-- MyComponent.vue -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
v-model
綁定利用剛才在 v-model
參數小節中學到的指定參數與事件名的技巧,我們可以在單個組件實例上創建多個 v-model
雙向綁定。
組件上的每一個 v-model
都會同步不同的 prop,而無需額外的選項:
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>
v-model
修飾符在學習輸入綁定時,我們知道了 v-model
有一些內置的修飾符,例如 .trim
,.number
和 .lazy
。在某些場景下,你可能想要一個自定義組件的 v-model
支持自定義的修飾符。
我們來創建一個自定義的修飾符 capitalize
,它會自動將 v-model
綁定輸入的字符串值第一個字母轉為大寫:
<MyComponent v-model.capitalize="myText" />
組件的 v-model
上所添加的修飾符,可以通過 modelModifiers
prop 在組件內訪問到。在下面的組件中,我們聲明了 modelModifiers
這個 prop,它的默認值是一個空對象:
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
defineEmits(['update:modelValue'])
console.log(props.modelModifiers) // { capitalize: true }
</script>
<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
注意這里組件的 modelModifiers
prop 包含了 capitalize
且其值為 true
,因為它在模板中的 v-model
綁定 v-model.capitalize="myText"
上被使用了。
有了這個 prop,我們就可以檢查 modelModifiers
對象的鍵,並編寫一個處理函數來改變拋出的值。在下面的代碼里,我們就是在每次 <input />
元素觸發 input
事件時將值的首字母大寫:
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>
v-model
修飾符對於又有參數又有修飾符的 v-model
綁定,生成的 prop 名將是 arg + "Modifiers"
。舉例來說:
<MyComponent v-model:title.capitalize="myText">
相應的聲明應該是:
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }
這里是另一個例子,展示了如何在使用多個不同參數的 v-model
時使用修飾符:
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
<script setup>
const props = defineProps({
firstName: String,
lastName: String,
firstNameModifiers: { default: () => ({}) },
lastNameModifiers: { default: () => ({}) }
})
defineEmits(['update:firstName', 'update:lastName'])
console.log(props.firstNameModifiers) // { capitalize: true }
console.log(props.lastNameModifiers) // { uppercase: true}
</script>