Skip to main content

Vue3 component 大進化

https://vuejs.org/guide/components/v-model.html

v-model on a component can also accept an argument:

template

<MyComponent v-model:title="bookTitle" />

In the child component, we can support the corresponding argument by passing a string to defineModel() as its first argument:

vue

<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>

<template>
<input type="text" v-model="title" />
</template>

If prop options are also needed, they should be passed after the model name:

const title = defineModel('title', { required: true })

基本用法

v-model 可以在元件上使用來實現雙向綁定。

從 Vue 3.4 開始,建議的方法是使用 defineModel() 巨集:

vue 視圖

<!-- Child.vue -->
<script setup>
const model = defineModel()

function update() {
model.value++
}
</script>

<template>
<div>Parent bound v-model is: {{ model }}</div>
</template>

然後父級可以用 v-model 綁定一個值:

template

<!-- Parent.vue -->
<Child v-model="countModel" />

defineModel() 傳回的值是一個引用。它可以像任何其他引用一樣被存取和改變,除了它充當父值和本地值之間的雙向綁定:

  • .value 與父級 v-model 綁定的值同步;
  • 當它被子進程改變時,它也會導致父進程的綁定值被更新。

usage:這表示您也可以使用 v-model 將此引用綁定到本機輸入元素,從而可以直接包裝本機輸入元素,同時提供相同的 v-model 用法:

vue 視圖

<script setup>
const model = defineModel()
</script>

<template>
<input v-model="model" />
</template>

引擎蓋下

defineModel 是一個方便的巨集。編譯器將其擴展為以下內容:

  • 名為 modelValue 的 prop,與本地引用的值同步;
  • 名為 update:modelValue 的事件,當本地引用的值發生變化時會發出該事件。

vue 視圖

<!-- 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="modelValue" 將被編譯為:

template

<!-- Parent.vue -->
<Child
:modelValue="foo"
@update:modelValue="$event => (foo = $event)"
/>

正如您所看到的,它更加冗長。然而,了解幕後發生的事情是有幫助的。

由於 defineModel 聲明了一個 prop,因此您可以透過將其傳遞給 defineModel 來聲明底層 prop 的選項:

// making the v-model required
const model = defineModel({ required: true })

// providing a default value
const model = defineModel({ default: 0 })

WARNING 警告 If you have a default value for defineModel prop and you don't provide any value for this prop from the parent component, it can cause a de-synchronization between parent and child components. In the example below, the parent's myRef is undefined, but the child's model is 1:如果 defineModel 屬性有一個 default 值,且您沒有從父元件為此屬性提供任何值,則可能會導致父元件和子元件之間不同步。在下面的範例中,父級的 myRef 未定義,但子級的 model 為 1:

// child component:
const model = defineModel({ default: 1 })

// parent component:
const myRef = ref()

html

<Child v-model="myRef"></Child>

v-model arguments

組件上的 v-model 也可以接受參數:

template

<MyComponent v-model:title="bookTitle" />

在子元件中,我們可以透過將字串傳遞給 defineModel() 作為其第一個參數來支援對應的參數:

vue 視圖

<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>

<template>
<input type="text" v-model="title" />
</template>

Try it in the Playground 在遊樂場嘗試一下

如果還需要 prop 選項,則應在模型名稱之後傳遞它們:

const title = defineModel('title', { required: true })

Multiple v-model bindings

透過利用我們先前透過 v-model 參數學習的針對特定道具和事件的能力,我們現在可以在單一元件實例上建立多個 v-model 綁定。

每個 v-model 將同步到不同的 prop,而不需要在元件中使用額外的選項:

template

<UserName
v-model:first-name="first"
v-model:last-name="last"
/>

vue 視圖

<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>

Try it in the Playground 在遊樂場嘗試一下

Handling v-model modifiers

When we were learning about form input bindings, we saw that v-model has built-in modifiers - .trim, .number and .lazy. In some cases, you might also want the v-model on your custom input component to support custom modifiers.當我們學習表單輸入綁定時,我們看到 v-model 有內建修飾符 - .trim.number.lazy。在某些情況下,您可能還想自訂輸入元件上的 v-model 支援自訂修飾符。

Let's create an example custom modifier, capitalize, that capitalizes the first letter of the string provided by the v-model binding:讓我們建立一個範例自訂修飾符 capitalize,它將 v-model 綁定提供的字串的第一個字母大寫:

template 範本

<MyComponent v-model.capitalize="myText" />

Modifiers added to a component v-model can be accessed in the child component by destructuring the defineModel() return value like this:新增至元件 v-model 的修飾符可以透過解構 defineModel() 傳回值在子元件中訪問,如下所示:

vue 視圖

<script setup>
const [model, modifiers] = defineModel()

console.log(modifiers) // { capitalize: true }
</script>

<template>
<input type="text" v-model="model" />
</template>

To conditionally adjust how the value should be read / written based on modifiers, we can pass get and set options to defineModel(). These two options receive the value on get / set of the model ref and should return a transformed value. This is how we can use the set option to implement the capitalize modifier:要根據修飾符有條件地調整值的讀取/寫入方式,我們可以將 getset 選項傳遞給 defineModel()。這兩個選項接收模型所引用的 get/set 值,並應傳回轉換後的值。這就是我們如何使用 set 選項來實作 capitalize 修飾符:

vue 視圖

<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>

Try it in the Playground 在遊樂場嘗試一下

Modifiers for v-model with arguments

Here's another example of using modifiers with multiple v-model with different arguments:這是使用具有不同參數的多個 v-model 修飾符的另一個範例:

template 範本

<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>

vue 視圖

<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')

console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>