Component v-model 之前有看過一些內容!
但還是不太清楚,希望這篇看完可以多了解一些
也太多v-xxx 要記住了吧 www~~
v-model
可以用於組件實現雙向綁定。
從 Vue 3.4 開始,推薦的方法是使用 defineModel()
巨集:
<!-- Child.vue -->
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>Parent bound v-model is: {{ model }}</div>
<button @click="update">Increment</button>
</template>
父組件可以使用 v-model
綁定一個值:
<!-- Parent.vue -->
<template>
<Child v-model="countModel" />
</template>
defineModel()
返回一個 ref
。它可以像其他 ref
一樣訪問和修改,但它作為父組件值和本地值之間的雙向綁定:
.value
與父組件 v-model
綁定的值同步;這意味著你也可以將這個 ref
綁定到原生輸入元素上,使用 v-model
,這使得包裝原生輸入元素變得簡單:
<script setup>
const model = defineModel()
</script>
<template>
<input v-model="model" />
</template>
defineModel
是一個方便的巨集。編譯器將其展開為:
modelValue
的 prop
,與本地 ref
的值同步;update:modelValue
的事件,當本地 ref
的值被修改時觸發。這是在 3.4 之前實現相同子組件的方法:
<!-- Child.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
然後,父組件中的 v-model="foo"
會被編譯為:
<!-- Parent.vue -->
<template>
<Child
:modelValue="foo"
@update:modelValue="$event => (foo = $event)"
/>
</template>
可以看到,這種方式更加冗長。然而,了解其底層運作方式是有幫助的。
因為 defineModel
聲明了一個 prop
,所以可以通過傳遞選項來聲明底層 prop
的選項:
// 使 v-model 成為必填項
const model = defineModel({ required: true })
// 提供默認值
const model = defineModel({ default: 0 })
如果 defineModel
prop
有默認值且父組件沒有提供任何值,這可能導致父子組件之間的不同步。在下面的例子中,父組件的 myRef
是未定義的,但子組件的 model
是 1:
// 子組件:
const model = defineModel({ default: 1 })
// 父組件:
const myRef = ref()
<Child v-model="myRef"></Child>
在組件上使用 v-model 也可以接受一個引數:
<!-- 父組件 -->
<MyComponent v-model:title="bookTitle" />
在子組件中,我們可以通過將字串作為 defineModel() 的第一個引數來支援相應的引數:
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>
<template>
<input type="text" v-model="title" />
</template>
如果需要 prop 選項,應在模型名稱之後傳遞它們:
const title = defineModel('title', { required: true })
<!-- MyComponent.vue -->
<script setup>
defineProps({
title: {
required: true
}
})
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
透過使用 v-model 參數的能力,我們可以在單個組件實例上創建多個 v-model 綁定。
每個 v-model 會同步到不同的 prop,而不需要在組件中添加額外的選項:
<template>
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
</template>
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>
<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
modifiers在學習表單輸入綁定時,我們知道 v-model 具有內建修飾符,例如 .trim
、.number
和 .lazy
。有時,我們可能希望自定義輸入組件的 v-model 支援自定義修飾符。
我們來創建一個名為 capitalize
的自定義修飾符,它會將 v-model 綁定的字符串的第一個字母大寫:
<template>
<MyComponent v-model.capitalize="myText" />
</template>
在子組件中,我們可以通過解構 defineModel()
的返回值來訪問添加到組件 v-model 的修飾符:
<script setup>
const [model, modifiers] = defineModel()
console.log(modifiers) // { capitalize: true }
</script>
<template>
<input type="text" v-model="model" />
</template>
為了根據修飾符有條件地調整如何讀取/寫入值,我們可以將 get
和 set
選項傳遞給 defineModel()
。這兩個選項在獲取/設置模型 ref 值時接收該值,並應返回轉換後的值。以下是使用 set
選項實現 capitalize
修飾符的方法:
<script setup>
const [model, modifiers] = defineModel({
set(value) {
if (modifiers.capitalize) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
return value
}
})
</script>
<template>
<input type="text" v-model="model" />
</template>
<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="props.modelValue" @input="emitValue" />
</template>
這是使用具有不同參數的多個 v-model 修飾符的另一個示例:
<template>
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
</template>
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')
console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>
<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>
v-model還是需要多練習,
看了3.4之前的用法,覺得3.4之後寫法很不錯啊!
頭暈暈地,到底差在哪?
v-model
的方式在 3.4 之前,使用 v-model
需要手動定義 props 和 emits:
<!-- MyComponent.vue -->
<script setup>
defineProps({
modelValue: String
})
defineEmits(['update:modelValue'])
</script>
<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
從 3.4 之後,使用 defineModel()
來自動化這個過程,簡化了代碼:
<!-- MyComponent.vue -->
<script setup>
const model = defineModel()
</script>
<template>
<input v-model="model" />
</template>
v-model
綁定在 3.4 之前,無法直接在同一組件上使用多個 v-model
。需要手動管理不同的 props 和 emits:
<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>
在 3.4 之後,可以使用 v-model
參數來支持多個綁定,讓語法更加清晰和簡潔:
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
在3.4之前,無法直接在 v-model
上使用自定義修飾符,需手動處理:
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
在3.4之後,可以直接在 v-model
上使用自定義修飾符,如 .capitalize
,並且可以透過 defineModel()
獲取修飾符:
<MyComponent v-model.capitalize="myText" />
<script setup>
const [model, modifiers] = defineModel()
v-model
,提供更大的靈活性。這些變化使得 Vue 的組件更容易開發和維護。