v-model создаёт **двустороннюю привязку** между элементом формы и реактивной переменной. Изменение в поле ввода сразу отражается в переменной, и наоборот — изменение переменной обновляет поле.
v-model — это синтаксический сахар над :value и @input:
<!-- Полная запись -->
<input :value="text" @input="text = $event.target.value">
<!-- Сокращение через v-model -->
<input v-model="text"><input v-model="username" type="text" placeholder="Имя пользователя">
<textarea v-model="bio" placeholder="О себе"></textarea>Одиночный чекбокс привязывается к булевой переменной:
<input v-model="agreed" type="checkbox">
<label>Согласен с условиями</label>
<!-- agreed: true / false -->Группа чекбоксов привязывается к массиву:
<input v-model="selected" type="checkbox" value="vue"> Vue
<input v-model="selected" type="checkbox" value="react"> React
<input v-model="selected" type="checkbox" value="angular"> Angular
<!-- selected: [] / ['vue'] / ['vue', 'react'] и т.д. --><input v-model="gender" type="radio" value="male"> Мужской
<input v-model="gender" type="radio" value="female"> Женский
<!-- gender: 'male' / 'female' --><select v-model="city">
<option value="">Выберите город</option>
<option value="moscow">Москва</option>
<option value="spb">Санкт-Петербург</option>
</select>
<!-- Multiple select -->
<select v-model="tags" multiple>
<option v-for="tag in allTags" :key="tag" :value="tag">{{ tag }}</option>
</select>По умолчанию v-model синхронизируется на каждое событие input. Модификатор .lazy переключает на событие change (после потери фокуса или нажатия Enter):
<!-- Синхронизируется только после потери фокуса -->
<input v-model.lazy="message">Автоматически преобразует введённое значение в число через parseFloat:
<input v-model.number="age" type="number">
<!-- age всегда будет числом, не строкой -->Автоматически обрезает пробелы в начале и конце строки:
<input v-model.trim="username">
<!-- ' Алексей ' -> 'Алексей' -->Модификаторы можно комбинировать:
<input v-model.lazy.trim="search"><template>
<form @submit.prevent="handleSubmit">
<input v-model.trim="form.name" placeholder="Имя">
<input v-model.number="form.age" type="number" placeholder="Возраст">
<input v-model.lazy="form.email" type="email" placeholder="Email">
<button type="submit">Отправить</button>
</form>
</template>
<script setup>
const form = reactive({ name: '', age: 0, email: '' })
function handleSubmit() {
console.log(form)
}
</script>Эмуляция v-model и его модификаторов .trim, .number, .lazy через обработчики событий
// v-model — это синтаксический сахар над value + событием.
// Эмулируем поведение v-model и его модификаторов на чистом JS.
// Базовый v-model: синхронизация при каждом вводе
function createVModel(initialValue) {
let value = initialValue
const listeners = []
return {
// getter (как :value)
get value() { return value },
// setter (как @input обработчик)
set value(newVal) {
value = newVal
listeners.forEach(fn => fn(value))
},
onChange(fn) { listeners.push(fn) },
// Имитируем ввод пользователя
simulateInput(rawInput) {
console.log(` Пользователь ввёл: "${rawInput}"`)
this.value = rawInput
}
}
}
// --- Обычный v-model ---
console.log('=== v-model (без модификаторов) ===')
const name = createVModel('')
name.onChange(v => console.log(` Значение обновлено: "${v}"`))
name.simulateInput('А')
name.simulateInput('Ал')
name.simulateInput('Алексей')
// --- v-model.trim ---
console.log('\n=== v-model.trim ===')
function createVModelTrim(initialValue) {
const model = createVModel(initialValue)
const originalInput = model.simulateInput.bind(model)
model.simulateInput = function(rawInput) {
console.log(` Пользователь ввёл: "${rawInput}"`)
model.value = rawInput.trim() // .trim применяется перед сохранением
}
return model
}
const username = createVModelTrim('')
username.onChange(v => console.log(` Значение (trimmed): "${v}"`))
username.simulateInput(' Борис ')
username.simulateInput(' Виктор')
// --- v-model.number ---
console.log('\n=== v-model.number ===')
function createVModelNumber(initialValue) {
const model = createVModel(initialValue)
model.simulateInput = function(rawInput) {
console.log(` Пользователь ввёл: "${rawInput}" (тип: ${typeof rawInput})`)
const parsed = parseFloat(rawInput)
model.value = isNaN(parsed) ? rawInput : parsed
}
return model
}
const age = createVModelNumber(0)
age.onChange(v => console.log(` Значение: ${v} (тип: ${typeof v})`))
age.simulateInput('25') // строка -> число 25
age.simulateInput('abc') // не число -> остаётся строкой
// --- v-model.lazy ---
console.log('\n=== v-model.lazy (обновление только после blur/change) ===')
function createVModelLazy(initialValue) {
const model = createVModel(initialValue)
let pendingValue = initialValue
model.simulateInput = function(rawInput) {
pendingValue = rawInput
console.log(` Печатает: "${rawInput}" (модель НЕ обновляется)`)
}
model.simulateBlur = function() {
console.log(` Потерял фокус — обновляем модель: "${pendingValue}"`)
model.value = pendingValue
}
return model
}
const message = createVModelLazy('')
message.onChange(v => console.log(` Модель обновлена: "${v}"`))
message.simulateInput('П')
message.simulateInput('Пр')
message.simulateInput('Привет')
message.simulateBlur() // только здесь обновляетсяv-model создаёт **двустороннюю привязку** между элементом формы и реактивной переменной. Изменение в поле ввода сразу отражается в переменной, и наоборот — изменение переменной обновляет поле.
v-model — это синтаксический сахар над :value и @input:
<!-- Полная запись -->
<input :value="text" @input="text = $event.target.value">
<!-- Сокращение через v-model -->
<input v-model="text"><input v-model="username" type="text" placeholder="Имя пользователя">
<textarea v-model="bio" placeholder="О себе"></textarea>Одиночный чекбокс привязывается к булевой переменной:
<input v-model="agreed" type="checkbox">
<label>Согласен с условиями</label>
<!-- agreed: true / false -->Группа чекбоксов привязывается к массиву:
<input v-model="selected" type="checkbox" value="vue"> Vue
<input v-model="selected" type="checkbox" value="react"> React
<input v-model="selected" type="checkbox" value="angular"> Angular
<!-- selected: [] / ['vue'] / ['vue', 'react'] и т.д. --><input v-model="gender" type="radio" value="male"> Мужской
<input v-model="gender" type="radio" value="female"> Женский
<!-- gender: 'male' / 'female' --><select v-model="city">
<option value="">Выберите город</option>
<option value="moscow">Москва</option>
<option value="spb">Санкт-Петербург</option>
</select>
<!-- Multiple select -->
<select v-model="tags" multiple>
<option v-for="tag in allTags" :key="tag" :value="tag">{{ tag }}</option>
</select>По умолчанию v-model синхронизируется на каждое событие input. Модификатор .lazy переключает на событие change (после потери фокуса или нажатия Enter):
<!-- Синхронизируется только после потери фокуса -->
<input v-model.lazy="message">Автоматически преобразует введённое значение в число через parseFloat:
<input v-model.number="age" type="number">
<!-- age всегда будет числом, не строкой -->Автоматически обрезает пробелы в начале и конце строки:
<input v-model.trim="username">
<!-- ' Алексей ' -> 'Алексей' -->Модификаторы можно комбинировать:
<input v-model.lazy.trim="search"><template>
<form @submit.prevent="handleSubmit">
<input v-model.trim="form.name" placeholder="Имя">
<input v-model.number="form.age" type="number" placeholder="Возраст">
<input v-model.lazy="form.email" type="email" placeholder="Email">
<button type="submit">Отправить</button>
</form>
</template>
<script setup>
const form = reactive({ name: '', age: 0, email: '' })
function handleSubmit() {
console.log(form)
}
</script>Эмуляция v-model и его модификаторов .trim, .number, .lazy через обработчики событий
// v-model — это синтаксический сахар над value + событием.
// Эмулируем поведение v-model и его модификаторов на чистом JS.
// Базовый v-model: синхронизация при каждом вводе
function createVModel(initialValue) {
let value = initialValue
const listeners = []
return {
// getter (как :value)
get value() { return value },
// setter (как @input обработчик)
set value(newVal) {
value = newVal
listeners.forEach(fn => fn(value))
},
onChange(fn) { listeners.push(fn) },
// Имитируем ввод пользователя
simulateInput(rawInput) {
console.log(` Пользователь ввёл: "${rawInput}"`)
this.value = rawInput
}
}
}
// --- Обычный v-model ---
console.log('=== v-model (без модификаторов) ===')
const name = createVModel('')
name.onChange(v => console.log(` Значение обновлено: "${v}"`))
name.simulateInput('А')
name.simulateInput('Ал')
name.simulateInput('Алексей')
// --- v-model.trim ---
console.log('\n=== v-model.trim ===')
function createVModelTrim(initialValue) {
const model = createVModel(initialValue)
const originalInput = model.simulateInput.bind(model)
model.simulateInput = function(rawInput) {
console.log(` Пользователь ввёл: "${rawInput}"`)
model.value = rawInput.trim() // .trim применяется перед сохранением
}
return model
}
const username = createVModelTrim('')
username.onChange(v => console.log(` Значение (trimmed): "${v}"`))
username.simulateInput(' Борис ')
username.simulateInput(' Виктор')
// --- v-model.number ---
console.log('\n=== v-model.number ===')
function createVModelNumber(initialValue) {
const model = createVModel(initialValue)
model.simulateInput = function(rawInput) {
console.log(` Пользователь ввёл: "${rawInput}" (тип: ${typeof rawInput})`)
const parsed = parseFloat(rawInput)
model.value = isNaN(parsed) ? rawInput : parsed
}
return model
}
const age = createVModelNumber(0)
age.onChange(v => console.log(` Значение: ${v} (тип: ${typeof v})`))
age.simulateInput('25') // строка -> число 25
age.simulateInput('abc') // не число -> остаётся строкой
// --- v-model.lazy ---
console.log('\n=== v-model.lazy (обновление только после blur/change) ===')
function createVModelLazy(initialValue) {
const model = createVModel(initialValue)
let pendingValue = initialValue
model.simulateInput = function(rawInput) {
pendingValue = rawInput
console.log(` Печатает: "${rawInput}" (модель НЕ обновляется)`)
}
model.simulateBlur = function() {
console.log(` Потерял фокус — обновляем модель: "${pendingValue}"`)
model.value = pendingValue
}
return model
}
const message = createVModelLazy('')
message.onChange(v => console.log(` Модель обновлена: "${v}"`))
message.simulateInput('П')
message.simulateInput('Пр')
message.simulateInput('Привет')
message.simulateBlur() // только здесь обновляетсяНапиши функцию `applyModifiers(value, modifiers)`, которая принимает строку и массив модификаторов (`"trim"`, `"number"`, `"uppercase"`) и последовательно применяет их. Если модификатор `"trim"` — вызывает `.trim()`. Если `"number"` — пытается преобразовать через `parseFloat`, если NaN — оставляет исходное значение. Если `"uppercase"` — переводит в верхний регистр. Модификаторы применяются в порядке массива.
Используй цикл `for...of modifiers` или `modifiers.reduce()`. Для каждого модификатора проверяй его значение через if/else и применяй соответствующее преобразование к переменной `result`.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке