A partir de Vue 3.4 y disponible en la actual versión 3.5+, defineModel() se ha convertido en una de las macros más potentes dentro del <script setup>. Esta nueva sintaxis te permite trabajar con v-model de forma más clara, concisa y tipada, eliminando la necesidad de declarar manualmente props y emits para la comunicación padre-hijo. Si estás trabajando con Vue 3.5+, ya deberías estar usándolo. ¿La clave? Menos boilerplate, más fluidez, y una integración impecable con los modifiers y múltiples v-model. ¡Dile adiós a modelValue verboso y hola a bindings con estilo!
)
defineModel()?Es una macro especial disponible únicamente en el contexto de <script setup>. Nos permite declarar una prop reactiva que está automáticamente conectada con el v-model del componente padre.
En resumen: menos código, menos errores, más claridad.
<!-- CustomInput.vue -->
<script setup lang="ts">
const model = defineModel({ type: String })
</script>
<template>
<input v-model="model" placeholder="Escribe algo..." />
</template><!-- ParentComponent.vue -->
<script setup lang="ts">
const nombre = ref('')
</script>
<template>
<CustomInput v-model="nombre" />
</template>Automáticamente:
Se declara la prop modelValue
Se emite update:modelValue al cambiar
¡No necesitas escribir props ni emits manualmente!
v-models: ¿Y si quiero algo más que modelValue?<!-- CounterComponent.vue -->
<script setup lang="ts">
const count = defineModel('count', { type: Number, default: 0 })
function incrementar() {
count.value++
}
</script>
<template>
<button @click="incrementar">Contador: {{ count }}</button>
</template><!-- ParentComponent.vue -->
<script setup lang="ts">
const clicks = ref(5)
</script>
<template>
<Counter v-model:count="clicks" />
</template>Un error común: declarar un valor por defecto en el hijo, pero no pasar nada desde el padre. Esto puede causar desincronización.
Consejo: si vas a usar
default, asegúrate de manejar correctamente el valor inicial desde el padre, o usar una sincronización manual si es necesario.
.trim, .number, .lazy...Sí, también funcionan. Se puede capturar y personalizar su comportamiento:
<script setup lang="ts">
const [texto, modifiers] = defineModel()
const [modelValue, modelModifiers] = defineModel({
set(value) {
if (modelModifiers.trim) {
return value.trim()
}
return value
}
})
</script>Ahora en el padre:
<MyInput v-model.trim="mensaje" />El
set()se encarga de aplicar.trimantes de enviar el valor modificado al padre.
defineModel() es un game changer para quienes usamos <script setup> en Vue 3.4+. Hace que trabajar con v-model en componentes sea más natural, más limpio y menos propenso a errores. Es ideal tanto para componentes simples como para interfaces complejas con múltiples bindings.