v-model на компоненті - це скорочення для пари «prop + подія оновлення». З Vue 3.4 для цього є макрос defineModel():
<!-- QuantityInput.vue -->
<script setup>
const quantity = defineModel({ type: Number, default: 1 });
</script>
<template>
<button type="button" @click="quantity--" :disabled="quantity <= 1">-</button>
<span>{{ quantity }}</span>
<button type="button" @click="quantity++">+</button>
</template>
<QuantityInput v-model="item.qty" />
defineModel() повертає ref: читання дає значення від батька, запис - надсилає подію update:modelValue, і батько оновлює свою змінну.
Що генерує компілятор (так писали до 3.4 і це варто розуміти):
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
// v-model="item.qty" на батьку = :modelValue="item.qty" @update:modelValue="v => item.qty = v"
Кілька v-model на одному компоненті - іменовані моделі:
<script setup>
const firstName = defineModel('firstName');
const lastName = defineModel('lastName');
</script>
<UserName v-model:first-name="user.first" v-model:last-name="user.last" />
Модифікатори (власні чи вбудовані на кшталт .trim) читаються з другого елемента й обробляються в set:
const [title, modifiers] = defineModel({
set(value) {
return modifiers.capitalize ? value.charAt(0).toUpperCase() + value.slice(1) : value;
},
});
Пастки:
- значення за замовчуванням в
defineModel({ default: ... })і відсутністьv-modelна батьку: дитина бачить своє значення, а батько -undefined. Стан розсинхронізований; - об'єкт у моделі: зміна поля
model.value.name = 'x'змінює об'єкт батька напряму, без події. Для об'єктів правильніше присвоювати новий об'єкт (model.value = { ...model.value, name: 'x' }) або мати окремі моделі на поля; - для нативного поля всередині - простіше прокинути
v-modelдалі:<input v-model="value">, деvalue = defineModel().