Custom v-model с разными имен свойств

Vue 3 предоставляет расширенные возможности для работы с v-model, включая возможность переименовывать модельные свойства и события, что особенно полезно при создании сложных компонентов с несколькими состояниями. Использование Custom v-model позволяет компоненту быть более гибким и интегрироваться с родительскими компонентами без жёсткой привязки к стандартному modelValue.


Стандартный v-model

По умолчанию v-model в Vue 3 связывает внутреннее состояние компонента с родительским через:

props: {
  modelValue: String
},
emits: ['update:modelValue']

В шаблоне родителя это выглядит так:

<CustomInput v-model="text" />

Внутри компонента можно обновлять значение:

this.$emit('update:modelValue', newValue);

Переименование v-model через prop и event

Vue 3 позволяет использовать несколько моделей одновременно, каждая с уникальным именем. Для этого в компоненте нужно явно указать имена пропсов и событий.

Пример:

props: {
  firstName: String,
  lastName: String
},
emits: ['update:firstName', 'update:lastName']

В родительском компоненте это связывается так:

<CustomForm v-model:first-name="userFirstName" v-model:last-name="userLastName" />

Важно помнить, что событие должно строго соответствовать шаблону update:имя_пропса.


Определение нескольких v-model в компоненте

Можно определить несколько v-model с разными именами в одном компоненте:

<script setup>
import { ref, watch } from 'vue'

defineProps({
  email: String,
  password: String
})

defineEmits(['update:email', 'update:password'])

const internalEmail = ref(email)
const internalPassword = ref(password)

watch(internalEmail, value => {
  emit('update:email', value)
})

watch(internalPassword, value => {
  emit('update:password', value)
})
</script>

<template>
  <input v-model="internalEmail" placeholder="Email" />
  <input v-model="internalPassword" placeholder="Password" type="password" />
</template>

Такой подход позволяет компоненту самостоятельно контролировать локальное состояние, не ломая двустороннюю связь с родителем.


Переименование v-model при экспорте компонента

Для более компактного использования в родителях можно задать кастомное имя v-model при экспорте:

<script>
export default {
  name: 'CustomInput',
  props: {
    valueText: String
  },
  emits: ['update:valueText']
}
</script>

Теперь в родительском шаблоне:

<CustomInput v-model:value-text="userInput" />

Это важно для избежания конфликта с другими v-model или стандартным modelValue.


Использование в комплексных формах

Когда компонент содержит несколько полей, каждое можно связать с отдельным v-model:

<MultiFieldForm 
  v-model:first-name="firstName" 
  v-model:last-name="lastName" 
  v-model:email="email" 
/>

Компонент при этом должен:

  1. Определять все props, соответствующие каждому v-model.
  2. Эмитить обновления через update:имя_пропса.
  3. При необходимости использовать внутренние состояния для управления формой.

Такой подход упрощает интеграцию сложных форм с внешними состояниями, сохраняя реактивность Vue.


Особенности и рекомендации

  • Имя пропса и имя события должны совпадать после update: (v-model:prop-nameupdate:propName).
  • Для множественных v-model использовать defineProps и defineEmits в <script setup> или стандартные опции в Options API.
  • Локальные переменные внутри компонента (ref) позволяют избежать непреднамеренного изменения внешнего состояния до подтверждения изменений.
  • Для динамического количества моделей можно использовать динамические ключи в emit, но это снижает читаемость и предсказуемость кода.

Пример комплексного компонента

<script setup>
import { ref, watch } from 'vue'

defineProps({
  username: String,
  age: Number
})

defineEmits(['update:username', 'update:age'])

const internalUsername = ref(username)
const internalAge = ref(age)

watch(internalUsername, value => emit('update:username', value))
watch(internalAge, value => emit('update:age', value))
</script>

<template>
  <input v-model="internalUsername" placeholder="Username" />
  <input v-model="internalAge" placeholder="Age" type="number" />
</template>

Родительский компонент:

<UserForm v-model:username="user.name" v-model:age="user.age" />

Такой подход гарантирует чистую двустороннюю связь и удобное управление состоянием без конфликтов с другими компонентами.


Этот механизм Custom v-model делает компоненты Vue гибкими, масштабируемыми и предсказуемыми, особенно при создании сложных интерфейсов с множественными формами и полями.