v-model на компонентах и defineModel

↑ 4.1 Vue 3: основы, компоненты, Composition API · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Как реализуется двусторонняя привязка на компонентах и что упростил defineModel.

Объяснение

v-model на компоненте — сахар: prop modelValue + событие update:modelValue.

<!-- Ручная реализация -->
<script setup>
const props = defineProps<{ modelValue: string }>();
const emit = defineEmits<{ "update:modelValue": [v: string] }>();
</script>
<template><input :value="modelValue" @input="emit('update:modelValue', ($event.target as HTMLInputElement).value)" /></template>
 
<!-- defineModel (3.4+): ref, синхронизированный с родителем -->
<script setup lang="ts">
const model = defineModel<string>({ default: "" });                 // v-model
const qty = defineModel<number>("qty", { required: true });         // v-model:qty
const [text, mods] = defineModel<string, "trim" | "upper">({
  set(v) { return mods.trim ? v.trim() : v; },                      // модификаторы v-model
});
</script>
<template><input v-model="model" /><button @click="qty++">+</button></template>
<SearchInput v-model="query" />
<Stepper v-model:qty="count" v-model:unit="unit" />
<TextField v-model.trim="name" />
ОсобенностьОписание
Несколько v-modelпо аргументу: v-model:title, v-model:size
Модификаторы.trim, .number, свои (modelModifiers)
defineModelвозвращает ref: запись = emit + локальная копия при отсутствии родителя
Native <input v-model>value + input (textarea — input, checkbox — change/checked, select — change)

Сложные поля: v-model для объекта — эмитьте новый объект (иммутабельно), а не мутируйте.

Нюансы и подводные камни

  • Нельзя привязать v-model к prop потомка напрямую (v-model="props.x").
  • В Vue 2 было value/input (и .sync): миграция на modelValue/update:modelValue.
  • Составные компоненты форм с v-model должны корректно работать с null/undefined.
  • Для <input v-model> с IME (ввод на CJK) обновление происходит после завершения композиции.

Практика

  1. Сделайте компонент TextField с defineModel, поддерживающий .trim.
  2. Реализуйте Stepper с двумя v-model.
  3. Перепишите старый компонент с ручным modelValue на defineModel.

Вопросы с ответами

Связанные темы