Vue Front-end Инженер

Vue Front-end Инженер

Роадмап навыков для прокачки

Двусторонняя привязка v-model

VueJSTemplate SyntaxДирективы

Основная идея

v-model — директива двусторонней привязки данных (two-way binding) во Vue. Она синхронизирует значение формы с переменной в JavaScript: изменения в поле ввода обновляют переменную, и наоборот.

Ключевые аспекты

  • Двусторонняя привязка — автоматическая синхронизация данных между UI и состоянием
  • Синтаксический сахар — эквивалент :value + @input для input, :checked + @change для checkbox
  • Модификаторы.lazy, .number, .trim изменяют поведение привязки
  • Работа с формами — input, textarea, select, checkbox, radio
  • Кастомные компоненты — можно использовать v-model на собственных компонентах

Плюсы

  • Минимум кода для работы с формами
  • Автоматическая синхронизация без ручного управления событиями
  • Встроенные модификаторы для типичных задач
  • Работает одинаково для разных типов элементов форм

Минусы

  • Скрывает реальную механику привязки от новичков
  • Для сложных сценариев может понадобиться ручная привязка
  • Разное поведение для разных типов input

Частые ошибки на собеседованиях

  • Не знают, что v-model это синтаксический сахар (:value + @input)
  • Путают v-model с v-bind (v-bind — односторонняя привязка)
  • Забывают про модификаторы .lazy, .number, .trim
  • Не понимают, как v-model работает с checkbox и radio (массивы, булевы значения)

Введение и проблематика

При работе с формами постоянно нужно:

  • Получать данные из полей ввода
  • Обновлять UI при изменении данных в коде
  • Синхронизировать состояние между JavaScript и DOM

Без специальных инструментов это требует ручного добавления обработчиков событий и обновления DOM. Vue решает эту задачу директивой v-model.

v-model — это синтаксический сахар. Для input он эквивалентен комбинации :value="data" и @input="data = $event.target.value".


Базовая теория

Текстовые поля

vue
<template>
  <input v-model="message" placeholder="Введите сообщение" />
  <p>Вы ввели: {{ message }}</p>
</template>
 
<script setup>
import { ref } from 'vue'
 
const message = ref('')
</script>

Как это работает под капотом

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

Практические примеры

Textarea

vue
<template>
  <textarea v-model="description" rows="4"></textarea>
  <p>Символов: {{ description.length }}</p>
</template>
 
<script setup>
import { ref } from 'vue'
 
const description = ref('')
</script>

Checkbox — одиночный (boolean)

vue
<template>
  <label>
    <input type="checkbox" v-model="isAgreed" />
    Я согласен с условиями
  </label>
  <p>Согласие: {{ isAgreed ? 'Да' : 'Нет' }}</p>
</template>
 
<script setup>
import { ref } from 'vue'
 
const isAgreed = ref(false)
</script>

Checkbox — множественный выбор (массив)

vue
<template>
  <label>
    <input type="checkbox" value="vue" v-model="skills" />
    Vue
  </label>
  <label>
    <input type="checkbox" value="react" v-model="skills" />
    React
  </label>
  <label>
    <input type="checkbox" value="angular" v-model="skills" />
    Angular
  </label>
  <p>Выбрано: {{ skills.join(', ') }}</p>
</template>
 
<script setup>
import { ref } from 'vue'
 
const skills = ref([])
</script>

Radio buttons

vue
<template>
  <label>
    <input type="radio" value="light" v-model="theme" />
    Светлая тема
  </label>
  <label>
    <input type="radio" value="dark" v-model="theme" />
    Тёмная тема
  </label>
  <p>Текущая тема: {{ theme }}</p>
</template>
 
<script setup>
import { ref } from 'vue'
 
const theme = ref('light')
</script>

Select

vue
<template>
  <!-- Одиночный выбор -->
  <select v-model="selectedCity">
    <option disabled value="">Выберите город</option>
    <option value="moscow">Москва</option>
    <option value="spb">Санкт-Петербург</option>
    <option value="kazan">Казань</option>
  </select>
 
  <!-- Множественный выбор -->
  <select v-model="selectedCities" multiple>
    <option value="moscow">Москва</option>
    <option value="spb">Санкт-Петербург</option>
    <option value="kazan">Казань</option>
  </select>
</template>
 
<script setup>
import { ref } from 'vue'
 
const selectedCity = ref('')
const selectedCities = ref([])
</script>

Модификаторы

.lazy — обновление по событию change

По умолчанию v-model обновляется при каждом вводе символа. Модификатор .lazy откладывает обновление до потери фокуса:

vue
<template>
  <!-- Обновляется при каждом символе -->
  <input v-model="instant" />
 
  <!-- Обновляется при потере фокуса или Enter -->
  <input v-model.lazy="delayed" />
</template>
 
<script setup>
import { ref } from 'vue'
 
const instant = ref('')
const delayed = ref('')
</script>

.number — преобразование в число

vue
<template>
  <input v-model.number="age" type="number" />
  <p>Тип: {{ typeof age }}</p> <!-- number -->
</template>
 
<script setup>
import { ref } from 'vue'
 
const age = ref(0)
</script>

.trim — удаление пробелов

vue
<template>
  <input v-model.trim="username" />
  <p>Длина: {{ username.length }}</p>
</template>
 
<script setup>
import { ref } from 'vue'
 
const username = ref('')
</script>

Комбинация модификаторов

vue
<template>
  <input v-model.lazy.trim="email" type="email" />
</template>

Пограничные кейсы

⚠️

Для checkbox с массивом важно инициализировать переменную как массив, иначе привязка будет работать некорректно.

vue
<script setup>
import { ref } from 'vue'
 
// ✅ Правильно — массив
const selectedItems = ref([])
 
// ❌ Неправильно — примитив для множественного выбора
const selectedItems = ref('')
</script>
🚫

Не используйте v-model с вычисляемым свойством без сеттера. Это вызовет ошибку при попытке изменить значение.

vue
<script setup>
import { ref, computed } from 'vue'
 
const firstName = ref('Иван')
const lastName = ref('Иванов')
 
// ❌ Нет сеттера — нельзя использовать с v-model
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
 
// ✅ С сеттером — можно использовать с v-model
const fullNameWritable = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (value) => {
    const [first, last] = value.split(' ')
    firstName.value = first
    lastName.value = last
  }
})
</script>

Кастомные значения для checkbox

vue
<template>
  <input
    type="checkbox"
    v-model="status"
    true-value="active"
    false-value="inactive"
  />
  <p>Статус: {{ status }}</p>
</template>
 
<script setup>
import { ref } from 'vue'
 
const status = ref('inactive')
</script>

Плюсы и минусы

Аспектv-modelРучная привязка
Количество кодаМинимумБольше
ЧитаемостьВысокаяСредняя
ГибкостьСтандартнаяПолная
КонтрольОграниченныйПолный
Типичные случаиИдеальноИзбыточно

Вопросы интервьюера

Q: Что делает v-model под капотом?

Для input это :value + @input. Привязывает значение и слушает событие ввода для обновления данных.

Q: Чем v-model отличается от v-bind?

v-bind — односторонняя привязка (данные → UI). v-model — двусторонняя (данные ↔ UI).

Q: Как работает v-model с checkbox?

Для одиночного checkbox привязывается boolean. Для множественного — массив значений выбранных чекбоксов.

Q: Когда использовать модификатор .lazy?

Когда не нужна мгновенная реакция на каждый символ. Например, для поиска с debounce или валидации по завершении ввода.


Источники