Skip to content
← Todas las preguntas
⚡ Intermedio

¿Qué es v-model y en qué se diferencia de .sync en Vue 2?

📌 Directivas🚚 Migración🔄 v-model

v-model crea un enlace bidireccional entre los datos del padre y un componente hijo (o un elemento de formulario). En elementos nativos es azúcar sintáctico para un binding :value más un listener @input. En componentes, vincula un prop y escucha un evento de actualización.

Diagrama de flujo de datos mostrando modelValue bajando y evento update subiendo

v-model en elementos nativos

vue
<template>
  <!-- Estas dos formas son equivalentes -->
  <input v-model="text" />
  <input
    :value="text"
    @input="text = ($event.target as HTMLInputElement).value"
  />
</template>

<script setup>
import { ref } from 'vue'
const text = ref('')
</script>
Open in Vue Playground

Distintos tipos de elementos usan diferentes pares prop/evento internamente:

ElementoProp vinculadoEvento
<input type="text">valueinput
<textarea>valueinput
<input type="checkbox">checkedchange
<input type="radio">checkedchange
<select>valuechange

v-model en componentes (Vue 3)

vue
<!-- Padre -->
<CustomInput v-model="search" />

<!-- Equivalente a: -->
<CustomInput :modelValue="search" @update:modelValue="search = $event" />
Open in Vue Playground

El componente recibe un prop modelValue y emite update:modelValue:

vue
<!-- CustomInput.vue (Vue 3.4+ con defineModel) -->
<script setup>
const model = defineModel<string>()
</script>

<template>
  <input v-model="model" />
</template>
Open in Vue Playground

Qué cambió respecto a Vue 2

En Vue 2, v-model usaba value + input y solo podías tener uno por componente. Para bindings bidireccionales adicionales, necesitabas el modificador .sync con nombres de prop distintos.

vue
<!-- Vue 2 -->
<MyDialog v-model="isOpen" :title.sync="dialogTitle" />

<!-- Vue 2 internamente: -->
<MyDialog
  :value="isOpen"
  @input="isOpen = $event"
  :title="dialogTitle"
  @update:title="dialogTitle = $event"
/>
Open in Vue Playground

En Vue 3, .sync fue eliminado. v-model ahora admite argumentos con nombre, por lo que puedes tener múltiples bindings sin una API separada:

vue
<!-- Vue 3 -->
<MyDialog v-model="isOpen" v-model:title="dialogTitle" />

<!-- Vue 3 internamente: -->
<MyDialog
  :modelValue="isOpen"
  @update:modelValue="isOpen = $event"
  :title="dialogTitle"
  @update:title="dialogTitle = $event"
/>
Open in Vue Playground

Resumen de la migración

Vue 2Vue 3
v-model vincula el prop valuev-model vincula el prop modelValue
Evento: inputEvento: update:modelValue
Un v-model por componenteMúltiples v-model con args con nombre
.sync para bindings bidireccionales extrav-model:propName con nombre
Opción model para personalizar prop/eventoNo es necesaria, usa v-model:name

Modificadores de v-model

Los modificadores integrados funcionan en elementos nativos:

vue
<input v-model.lazy="msg" />
<!-- sincroniza en change, no en input -->
<input v-model.number="age" />
<!-- convierte a número -->
<input v-model.trim="name" />
<!-- elimina espacios en blanco -->
Open in Vue Playground

Los componentes pueden definir modificadores personalizados con defineModel:

vue
<script setup>
const [model, modifiers] =
  defineModel <
  string >
  {
    set(value) {
      if (modifiers.capitalize) {
        return value.charAt(0).toUpperCase() + value.slice(1)
      }
      return value
    }
  }
</script>
Open in Vue Playground
vue
<!-- Padre -->
<CustomInput v-model.capitalize="text" />
Open in Vue Playground

Ver también: Usar múltiples bindings v-model · ¿Cómo funciona v-model en componentes personalizados? · ¿Cómo se crean modificadores v-model personalizados?

Referencias

Publicado bajo la licencia MIT.