v-model в Vue.js
Что такое v-model?
v-model — это директива Vue, которая создаёт двустороннее связывание данных (two-way binding) между элементом формы и данными компонента.
<template>
<input v-model="message" />
<p>Сообщение: {{ message }}</p>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('Привет')
</script>
Когда пользователь вводит текст, message обновляется автоматически, и наоборот.
Как работает v-model?
v-model — это синтаксический сахар над связыванием значения и обработкой события:
<!-- С v-model -->
<input v-model="text" />
<!-- Эквивалентно -->
<input
:value="text"
@input="text = $event.target.value"
/>
Vue автоматически:
- Связывает значение с данными (
:value) - Слушает событие изменения (
@input) - Обновляет данные при изменении
v-model с разными элементами
Input (text)
<template>
<input v-model="text" type="text" />
</template>
<script setup>
import { ref } from 'vue'
const text = ref('')
</script>
Textarea
<template>
<textarea v-model="message"></textarea>
<p>{{ message }}</p>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('Многострочный текст')
</script>
Checkbox (один элемент)
<template>
<input type="checkbox" v-model="checked" />
<span>Согласен: {{ checked }}</span>
</template>
<script setup>
import { ref } from 'vue'
const checked = ref(false)
</script>
Checkbox (несколько элементов)
<template>
<input type="checkbox" value="Vue" v-model="frameworks" />
<input type="checkbox" value="React" v-model="frameworks" />
<input type="checkbox" value="Angular" v-model="frameworks" />
<p>Выбрано: {{ frameworks }}</p>
</template>
<script setup>
import { ref } from 'vue'
const frameworks = ref([])
</script>
Radio
<template>
<input type="radio" value="male" v-model="gender" />
<input type="radio" value="female" v-model="gender" />
<p>Пол: {{ gender }}</p>
</template>
<script setup>
import { ref } from 'vue'
const gender = ref('male')
</script>
Select
<template>
<select v-model="selected">
<option disabled value="">Выберите</option>
<option value="A">Опция A</option>
<option value="B">Опция B</option>
<option value="C">Опция C</option>
</select>
<p>Выбрано: {{ selected }}</p>
</template>
<script setup>
import { ref } from 'vue'
const selected = ref('')
</script>
Select (multiple)
<template>
<select v-model="selected" multiple>
<option value="A">Опция A</option>
<option value="B">Опция B</option>
<option value="C">Опция C</option>
</select>
<p>Выбрано: {{ selected }}</p>
</template>
<script setup>
import { ref } from 'vue'
const selected = ref([])
</script>
Модификаторы v-model
.lazy
По умолчанию v-model обновляется на событие input. Модификатор .lazy переключает на change:
<template>
<!-- Обновится только после потери фокуса -->
<input v-model.lazy="message" />
</template>
.number
Автоматически преобразует значение в число:
<template>
<input v-model.number="age" type="number" />
</template>
<script setup>
import { ref } from 'vue'
const age = ref(0)
// age всегда будет числом, а не строкой
</script>
.trim
Автоматически удаляет пробелы в начале и конце:
<template>
<input v-model.trim="username" />
</template>
<script setup>
import { ref } from 'vue'
const username = ref('')
</script>
Комбинирование модификаторов
<template>
<input v-model.lazy.trim="message" />
<input v-model.number.lazy="age" type="number" />
</template>
v-model в пользовательских компонентах
Options API
В Options API v-model работает с prop modelValue и событием update:modelValue:
<!-- ParentComponent.vue -->
<template>
<CustomInput v-model="searchText" />
</template>
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
const searchText = ref('')
</script>
<!-- CustomInput.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue']
}
</script>
Composition API
<!-- CustomInput.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
Вычисляемое свойство для упрощения
<!-- CustomInput.vue -->
<template>
<input v-model="value" />
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>
Множественный v-model (Vue 3)
В Vue 3 можно использовать несколько v-model на одном компоненте:
<!-- UserForm.vue -->
<template>
<UserName
v-model:first-name="firstName"
v-model:last-name="lastName"
/>
</template>
<script setup>
import { ref } from 'vue'
const firstName = ref('')
const lastName = ref('')
</script>
<!-- UserName.vue -->
<template>
<input
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
placeholder="Имя"
/>
<input
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
placeholder="Фамилия"
/>
</template>
<script setup>
defineProps(['firstName', 'lastName'])
defineEmits(['update:firstName', 'update:lastName'])
</script>
Модификаторы в пользовательских компонентах
Можно создавать собственные модификаторы:
<!-- Parent.vue -->
<template>
<MyComponent v-model.capitalize="myText" />
</template>
<!-- MyComponent.vue -->
<template>
<input
:value="modelValue"
@input="handleInput"
/>
</template>
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: {
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue'])
function handleInput(event) {
let value = event.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
v-model vs :value + @input
Когда использовать v-model
<!-- Простой случай -->
<input v-model="username" />
Когда использовать :value + @input
<!-- Нужна дополнительная логика -->
<input
:value="username"
@input="handleInput"
/>
<script setup>
function handleInput(event) {
const value = event.target.value
// Дополнительная валидация
if (value.length <= 20) {
username.value = value
}
}
</script>
Практические примеры
Форма регистрации
<template>
<form @submit.prevent="handleSubmit">
<input
v-model.trim="form.username"
placeholder="Имя пользователя"
/>
<input
v-model.trim="form.email"
type="email"
placeholder="Email"
/>
<input
v-model.number="form.age"
type="number"
placeholder="Возраст"
/>
<select v-model="form.country">
<option value="">Выберите страну</option>
<option value="ru">Россия</option>
<option value="us">США</option>
</select>
<label>
<input type="checkbox" v-model="form.agree" />
Согласен с условиями
</label>
<button :disabled="!form.agree">Зарегистрироваться</button>
</form>
</template>
<script setup>
import { reactive } from 'vue'
const form = reactive({
username: '',
email: '',
age: 0,
country: '',
agree: false
})
function handleSubmit() {
console.log('Form data:', form)
}
</script>
Поиск с debounce
<template>
<input v-model="searchQuery" placeholder="Поиск..." />
<p>Ищем: {{ debouncedQuery }}</p>
</template>
<script setup>
import { ref, watch } from 'vue'
const searchQuery = ref('')
const debouncedQuery = ref('')
let timeoutId = null
watch(searchQuery, (newValue) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
debouncedQuery.value = newValue
// Здесь можно выполнить запрос к API
}, 500)
})
</script>
Частые ошибки
Изменение props напрямую
<!-- Неправильно -->
<template>
<input v-model="modelValue" />
</template>
<script setup>
defineProps(['modelValue'])
// Vue предупредит об изменении props
</script>
<!-- Правильно -->
<template>
<input v-model="value" />
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
</script>
Использование v-model на компонентах без правильных props/emits
<!-- Неправильно - компонент не получит данные -->
<CustomComponent v-model="data" />
<!-- Правильно - компонент должен принимать modelValue -->
<script setup>
// В CustomComponent
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
Вывод
v-model:
- Создаёт двустороннее связывание данных
- Синтаксический сахар над
:valueи@input - Работает с разными элементами форм
- Имеет модификаторы:
.lazy,.number,.trim - В Vue 3 поддерживает множественное использование
- Можно использовать в пользовательских компонентах
На собеседовании:
Важно уметь:
- Объяснить, что такое v-model и как он работает
- Показать, как v-model раскрывается в :value + @input
- Рассказать о модификаторах
- Объяснить, как использовать v-model в пользовательских компонентах
- Привести примеры использования с разными элементами форм