Vue 3.4 Новая механика v-model компонента
Краткий перевод: https://vuejs.org/guide/components/v-model.html
Основное использование
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> </template>
Затем родительский компонент может связать значение с v-model
:
<!-- Parent.vue --> <Child v-model="count" />
Значение, возвращаемое методом defineModel()
, является реактивной переменной ref. К нему можно получить доступ и изменить его, как и к любой другой ref, за исключением того, что он действует как двусторонняя привязка между родительским значением и локальным значением:
- Его .value синхронизируется со значением, связанным родительской
v-model
; - Когда .value изменяется внутри дочернего элемента, это также приводит к обновлению значения внутри родительского элемента.
Это означает, что вы также можете привязать этот ref нативному элементу ввода (input) с помощью v-model
, что упрощает расположение элементов ввода input (снаружи или внутри родительского компонента), обеспечивая при этом такое же использование v-model
:
<script setup> const model = defineModel() </script> <template> <input v-model="model" /> </template>
Под капотом
defineModel
это удобный макрос. Компилятор расширяет его до следующего::
- Создает prop с именем modelValue, с которой синхронизируется локальное значение ref;
- Создает событие с именем update:modelValue, которое генерируется при изменении значения локальной ref.
Вот как вы могли бы реализовать тот же дочерний компонент, показанный выше до версии 3.4:
<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>
Как видите, это немного более многословно. Однако полезно знать, что происходит под капотом.
Так же поскольку defineModel объявляет prop, вы можете указать параметры свойства prop, передав их в defineModel:
// making the v-model required const model = defineModel({ required: true }) // providing a default value const model = defineModel({ default: 0 })а
Аргументы v-model
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 })
Множественное использование v-model
Ранее мы рассмотрели использование одного prop и события, с помощью аргументов v-model
, но вы так же можете создавать несколько привязок v-model
для одного экземпляра компонента.
Каждая v-model
будет синхронизироваться со своим prop без необходимости использования дополнительных параметров в компоненте:
<UserName v-model:first-name="first" v-model:last-name="last" />
<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>
Использование модификаторов v-model
В некоторых случаях вам также может потребоваться, чтобы v-model
вашего компонента ввода (input) поддерживала стандартные пользовательские модификаторы (например такие как .trim
, .number
.lazy
).
Пример использования модификатора capitalize
, который делать первую букву строки всегда заглавной в родительском компоненте:
<MyComponent v-model.capitalize="myText" />
Доступ к модификаторам, добавленным к v-model компонента, можно получить в дочернем компоненте, деструктурировав возвращаемое значение defineModel() следующим образом:
<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>
Модификаторы для v-model
с аргументами
Вот еще один пример использования модификаторов с несколькими v-model
с разными аргументами:
<UserName v-model:first-name.capitalize="first" v-model:last-name.uppercase="last" />
<script setup> const [firstName, firstNameModifiers] = defineModel('firstName') const [lastName, lastNameModifiers] = defineModel('lastName') console.log(firstNameModifiers) // { capitalize: true } console.log(lastNameModifiers) // { uppercase: true} </script>