0% encontró este documento útil (0 votos)
4 vistas139 páginas

VUE Composition API

Vue.js es un framework de JavaScript que permite el desarrollo de interfaces de usuario mediante un enfoque basado en componentes y renderizado declarativo. La introducción de la Composition API en Vue 3 mejora la organización y reutilización del código, facilitando la gestión de componentes complejos. Los principios fundamentales incluyen reactividad, modularidad y un sistema de hooks que optimiza la experiencia del desarrollador.

Cargado por

juan mansilla
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
4 vistas139 páginas

VUE Composition API

Vue.js es un framework de JavaScript que permite el desarrollo de interfaces de usuario mediante un enfoque basado en componentes y renderizado declarativo. La introducción de la Composition API en Vue 3 mejora la organización y reutilización del código, facilitando la gestión de componentes complejos. Los principios fundamentales incluyen reactividad, modularidad y un sistema de hooks que optimiza la experiencia del desarrollador.

Cargado por

juan mansilla
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Capítulo 1: Introducción a Vue.

js (con Composi on API)

[Link] es un framework de JavaScript progresivo que ha transformado el desarrollo de interfaces


de usuario para aplicaciones web

. Se dis ngue por su adaptabilidad, permi endo una "adopción incremental" donde los
desarrolladores pueden integrarlo en la medida necesaria en cualquier proyecto, desde pequeños
proto pos hasta grandes aplicaciones empresariales

En su esencia, [Link] se enfoca en el renderizado declara vo y una arquitectura basada en


componentes

. Esto significa que, en lugar de detallar cada paso para manipular el DOM (Document Object
Model) de forma impera va, los desarrolladores describen el "resultado deseado", y Vue se
encarga de los detalles de implementación. La arquitectura de componentes, por su parte,
fomenta la creación de elementos de interfaz de usuario (UI) reu lizables y autocontenidos, lo que
facilita la ges ón de aplicaciones complejas al dividirlas en piezas modulares

Creado por Evan You en 2014, [Link] fue diseñado para capturar los mejores aspectos de otros
frameworks, eliminando la complejidad innecesaria. Su núcleo se concentra en la capa de vista, lo
que permite una integración sencilla con otros proyectos o librerías

La Revolución de la Composi on API en Vue 3

Con la llegada de Vue 3, el framework introdujo una nueva forma poderosa de organizar la lógica
de los componentes: la Composi on API

. Esta API fue diseñada para abordar las limitaciones de los enfoques tradicionales, par cularmente
en componentes complejos donde la lógica relacionada con una caracterís ca específica podía
encontrarse dispersa en diferentes secciones del código

La Composi on API permite agrupar el código por "preocupación lógica" en lugar de por po de
opción (como tradicionalmente se hacía con data, methods, computed)

. Esto se traduce en una mejor organización del código, una reu lización más flexible de la lógica
a través de "composables" (funciones reu lizables con estado) y una mayor transparencia y
mantenibilidad. Además, Vue 3 rediseñó su sistema de reac vidad u lizando ES6 Proxies, lo que
ofrece un mejor rendimiento y menos limitaciones que versiones anteriores

Principios Fundamentales de Vue con Composi on API


1. Renderizado Declara vo y Reac vidad: [Link] u liza una sintaxis de plan llas que ex ende el
HTML con direc vas, aplicando un comportamiento reac vo al DOM

. Esto significa que, cuando tus datos cambian, Vue actualiza automá camente la interfaz de
usuario.

2. Para definir datos reac vos en la Composi on API, u lizamos funciones como ref o reac ve
dentro de la función setup() de un componente (o directamente en <script setup>).

3. Ejemplo Básico de Renderizado Declara vo con Composi on API:

4. src/components/Gree [Link]

5. Y en tu archivo [Link] o vista principal:

6. En este ejemplo, cuando el valor de message cambie (por ejemplo, [Link] = '¡Hola
Mundo!'), Vue automá camente detectará el cambio y actualizará el texto en el DOM, gracias a su
sistema de reac vidad basado en Proxy

7. Arquitectura Basada en Componentes: Los componentes son los bloques de construcción de


una aplicación [Link], encapsulando su propio HTML (<template>), JavaScript (<script>) y CSS
(<style>) en un solo "Componente de Archivo Único" (SFC por sus siglas en inglés, .vue)

8. Ejemplo de un Componente Básico con Composi on API (<script setup>):

9. src/components/UserProfi[Link]

10. En este ejemplo, el componente UserProfile define su propio estado (isFollowing), una
propiedad computada (displayMessage) y un método (followUser) u lizando la Composi on API
dentro de la sintaxis <script setup>

. Recibe datos (user) a través de defineProps y emite un comportamiento basado en la interacción


del usuario.

Caracterís cas Clave de la Composi on API

• setup() / <script setup>: Es el punto de entrada para la lógica de la Composi on API en un


componente

. Permite declarar el estado reac vo, métodos, propiedades computadas y hooks del ciclo de vida
de forma agrupada por funcionalidad. La sintaxis <script setup> es una mejora en Vue 3 que
reduce la verbosidad y mejora la inferencia de pos con TypeScript

• Primi vas Reac vas (ref, reac ve, computed):

◦ ref(): Se usa para hacer que cualquier po de dato (primi vos como strings, numbers,
booleans; o incluso objetos) sea reac vo. Se accede a su valor a través de la propiedad .value
.

◦ reac ve(): Convierte un objeto en un proxy reac vo. Las propiedades del objeto se acceden
directamente sin .value

◦ computed(): Crea propiedades que se calculan a par r de otras propiedades reac vas y se
almacenan en caché, actualizándose solo cuando sus dependencias cambian

• Hooks del Ciclo de Vida: Los hooks como onMounted, onUpdated, onBeforeUnmount, etc., se
importan y llaman directamente dentro de <script setup>, permi endo ejecutar código en
momentos específicos de la vida del componente

• Composables: Son funciones reu lizables que encapsulan lógica con estado. Son la base para la
extracción y el aislamiento de funcionalidades, promoviendo una gran reu lización de código

Beneficios de Vue con Composi on API

La combinación de [Link] y la Composi on API en Vue 3 proporciona una base sólida para el
desarrollo web moderno

• Simplicidad y Facilidad de Aprendizaje: Para desarrolladores web con experiencia en HTML, CSS
y JavaScript, Vue ofrece una curva de aprendizaje suave, especialmente con su sintaxis de plan llas
intui va y el enfoque claro de la Composi on API

• Rendimiento Óp mo: Gracias al uso del DOM virtual y un sistema de reac vidad eficiente
basado en ES6 Proxies, Vue 3 minimiza las operaciones costosas en el DOM, resultando en
aplicaciones rápidas y ligeras

• Modularidad y Reu lización: La arquitectura basada en componentes y los composables de la


Composi on API facilitan la división de la aplicación en piezas manejables y reu lizables,
mejorando la mantenibilidad y escalabilidad

• Escalabilidad: La Composi on API permite organizar la lógica de componentes complejos de


manera mucho más efec va, lo que es crucial para el crecimiento y mantenimiento de grandes
aplicaciones

.
• Excelente Experiencia de Desarrollador (DX): Con herramientas como Vue DevTools, mensajes
de error detallados y una documentación exhaus va, [Link] garan za una experiencia de
desarrollo produc va y agradable

• Fuerte Integración con TypeScript: Vue 3 fue reescrito en TypeScript desde cero, y la
Composi on API (especialmente con <script setup>) ofrece una inferencia de pos y seguridad
inigualables, lo que ayuda a prevenir errores en empo de compilación y mejora la calidad del
código

Perfecto . Te armo los ejemplos y el código documentado para el Capítulo 1: Introducción a


[Link] (con Composi on API), siempre usando <script setup> (la forma moderna y recomendada).

Capítulo 1: Introducción a [Link] (con Composi on API)

1. Renderizado Declara vo y Reac vidad

Ejemplo básico con ref:

src/components/Gree [Link]

<template>

<!-- Renderizado declara vo: Vue actualiza automá camente el DOM cuando cambia message -->

<div>

<h2>{{ message }}</h2>

<bu on @click="changeMessage">Cambiar mensaje</bu on>

</div>

</template>

<script setup>

// Importamos ref para crear un dato reac vo

import { ref } from 'vue'

// message es un valor reac vo gracias a ref()

const message = ref('¡Bienvenido a Vue 3 con Composi on API!')


// Método que cambia el mensaje

func on changeMessage() {

[Link] = '¡Hola Mundo!'

</script>

src/[Link]

<template>

<div>

<h1>Mi Primera App con Vue 3</h1>

<Gree ngDisplay />

</div>

</template>

<script setup>

import Gree ngDisplay from './components/Gree [Link]'

</script>

Aquí, cuando [Link] cambia, Vue actualiza automá camente el DOM.

2. Arquitectura Basada en Componentes

Ejemplo con props, computed y ref:

src/components/UserProfi[Link]

<template>

<div class="profile-card">

<h2>{{ [Link] }}</h2>

<p>Edad: {{ [Link] }}</p>

<p>{{ displayMessage }}</p>

<bu on @click="followUser">

{{ isFollowing ? 'Dejar de seguir' : 'Seguir' }}

</bu on>
</div>

</template>

<script setup>

// Importamos primi vas reac vas

import { ref, computed } from 'vue'

// Props: datos que vienen del padre

const props = defineProps({

user: {

type: Object,

required: true

})

// Estado interno del componente

const isFollowing = ref(false)

// Computed: cambia dinámicamente según isFollowing

const displayMessage = computed(() =>

[Link]

? `Ahora sigues a ${[Link]}`

: `¿Quieres seguir a ${[Link]}?`

// Método: cambia el estado de seguimiento

func on followUser() {

[Link] = ![Link]

}
</script>

<style scoped>

.profile-card {

border: 1px solid #ddd;

padding: 1rem;

border-radius: 12px;

width: 250px;

</style>

src/[Link]

<template>

<div>

<UserProfile :user="{ name: 'Ana Pérez', age: 29 }" />

</div>

</template>

<script setup>

import UserProfile from './components/UserProfi[Link]'

</script>

Aquí tenemos:

 Props (user) → entrada de datos desde el padre.

 Ref (isFollowing) → estado interno reac vo.

 Computed (displayMessage) → lógica derivada.

 Método (followUser) → acción que modifica el estado.

3. Hooks del Ciclo de Vida con Composi on API

src/components/[Link]

<template>
<div>

<h2>Tiempo transcurrido: {{ seconds }} segundos</h2>

</div>

</template>

<script setup>

import { ref, onMounted, onBeforeUnmount } from 'vue'

const seconds = ref(0)

let intervalId = null

// Hook: se ejecuta cuando el componente se monta

onMounted(() => {

intervalId = setInterval(() => {

[Link]++

}, 1000)

})

// Hook: se ejecuta antes de desmontar el componente

onBeforeUnmount(() => {

clearInterval(intervalId)

})

</script>

Uso de hooks: onMounted inicializa un intervalo y onBeforeUnmount limpia la memoria.

4. Composables (reu lización de lógica)

src/composables/[Link]

import { ref } from 'vue'


// Un composable es simplemente una función que encapsula lógica con estado

export func on useCounter(ini alValue = 0) {

const count = ref(ini alValue)

const increment = () => [Link]++

const decrement = () => [Link]--

const reset = () => ([Link] = ini alValue)

return { count, increment, decrement, reset }

src/components/[Link]

<template>

<div>

<h2>Contador: {{ count }}</h2>

<bu on @click="increment">+</bu on>

<bu on @click="decrement">-</bu on>

<bu on @click="reset">Reset</bu on>

</div>

</template>

<script setup>

// Importamos nuestro composable

import { useCounter } from '../composables/useCounter'

const { count, increment, decrement, reset } = useCounter(5)

</script>

Ahora la lógica del contador puede reu lizarse en múl ples componentes.

Resumen de este capítulo:


1. Declara ve Rendering + Reac vity → ref, reac ve, computed.

2. Component-Based Architecture → componentes con <template>, <script setup>, <style>.

3. Lifecycle Hooks → onMounted, onBeforeUnmount.

4. Composables → funciones reu lizables con estado.

¿Quieres que arme también un ejemplo final integrador (mini app con Gree ngDisplay +
UserProfile + Timer + Counter usando un layout común en [Link]), como cierre del capítulo?
Capítulo 2: Interfaces Interac vas (con Composi on API)

Las interfaces interac vas son el corazón de las aplicaciones web modernas, permi endo a los
usuarios interactuar dinámicamente con el contenido y recibir respuestas instantáneas. [Link], con
su Composi on API, proporciona un conjunto de herramientas potente y flexible para construir
estas experiencias de usuario, transformando datos está cos en elementos dinámicos y recep vos

La Composi on API, introducida en Vue 3, facilita la organización de la lógica de los componentes


de forma más coherente y reu lizable, agrupando el código por "preocupación lógica"

. Esto es especialmente ventajoso para manejar interacciones complejas, donde la reac vidad, el
manejo de eventos y las actualizaciones condicionales trabajan en armonía.

La Reac vidad como Fundamento

En Vue, la interac vidad comienza con la reac vidad. Las primi vas reac vas como ref y reac ve
son la base sobre la cual se construyen todas las interacciones de la UI

. Cuando el estado reac vo cambia, Vue automá camente actualiza el DOM de forma eficiente,
gracias a su sistema de reac vidad basado en ES6 Proxies

• ref(): Convierte un valor (primi vo o de objeto) en una referencia reac va. Su valor se accede a
través de la propiedad .value

• reac ve(): Convierte un objeto en un proxy reac vo, donde las propiedades se acceden
directamente

• computed(): Crea propiedades derivadas que se actualizan automá camente solo cuando sus
dependencias reac vas cambian, y su resultado se almacena en caché

Ejemplo Básico de Reac vidad con Composi on API:

<template>

<div>

<p>Contador: {{ count }}</p>

<p>Doble del contador: {{ doubleCount }}</p>

<bu on @click="increment">Incrementar</bu on>

</div>
</template>

<script setup>

import { ref, computed } from 'vue';

// Declaramos un estado reac vo 'count'

const count = ref(0);

// Definimos una propiedad computada 'doubleCount' que depende de 'count'

const doubleCount = computed(() => [Link] * 2);

// Función para modificar el estado

func on increment() {

[Link]++; // Vue detecta el cambio y actualiza el DOM y las computadas dependientes

</script>

Manejo de Eventos (v-on / @)

Los usuarios interactúan con las aplicaciones a través de eventos (clics, entradas de teclado, envíos
de formularios, etc.). La direc va v-on (o su atajo @) permite adjuntar oyentes de eventos a
elementos del DOM

Vue simplifica el manejo de eventos con modificadores de eventos, que son sufijos con punto que
se añaden a los eventos para realizar acciones comunes como detener la propagación (.stop),
prevenir el comportamiento predeterminado (.prevent) o ejecutar una acción solo una vez (.once)

Ejemplo de Manejo de Eventos con Modificadores en Composi on API:

<template>

<div>

<bu on @click="handleClick">Haz clic</bu on>

<bu on @[Link]="handleClickStopped">Clic (propagación detenida)</bu on>


<form @[Link]="handleSubmit">

<input type="text" v-model="formInput" />

<bu on type="submit">Enviar (prevent default)</bu on>

</form>

<p>Input: {{ formInput }}</p>

</div>

</template>

<script setup>

import { ref } from 'vue';

const formInput = ref('');

func on handleClick() {

alert('Botón clicado!');

func on handleClickStopped() {

alert('Clic detenido! No se propagará más.');

func on handleSubmit() {

alert(`Formulario enviado con: ${[Link]}`);

// Lógica de envío de formulario

</script>

Renderizado Condicional (v-if, v-show)

El renderizado condicional permite mostrar u ocultar elementos del DOM basándose en el estado
de la aplicación
. Vue ofrece dos direc vas principales para esto:

• v-if (y v-else-if, v-else): Rendeiza el elemento de forma condicional, añadiéndolo o eliminándolo


del DOM real

. Esto es ú l cuando los elementos se alternan con poca frecuencia o cuando su existencia afecta
significa vamente la lógica o el rendimiento. Puedes usar el elemento <template> con v-if para
renderizar condicionalmente múl ples elementos sin añadir un wrapper innecesario al DOM

• v-show: Simplemente alterna la propiedad display de CSS del elemento, manteniéndolo siempre
en el DOM. Es más adecuado para elementos que se alternan con frecuencia, ya que la sobrecarga
inicial es mayor, pero el costo de alternar es menor.

Ejemplo de Renderizado Condicional con Composi on API:

<template>

<div>

<bu on @click="toggleVisibility">Alternar elementos</bu on>

<!-- Usando v-if / v-else-if / v-else -->

<div v-if="isVisible === 'A'">

<p>Este es el Contenido A (usando v-if)</p>

</div>

<div v-else-if="isVisible === 'B'">

<p>Este es el Contenido B (usando v-else-if)</p>

</div>

<div v-else>

<p>Contenido por defecto (usando v-else)</p>

</div>

<!-- Usando v-show -->

<p v-show="showWithVShow">Este texto se muestra/oculta con v-show</p>

<!-- Usando <template> con v-if para múl ples elementos -->

<template v-if="showTemplateContent">
<h3>Título condicional</h3>

<p>Párrafo condicional 1</p>

<p>Párrafo condicional 2</p>

</template>

</div>

</template>

<script setup>

import { ref } from 'vue';

const isVisible = ref('A'); // Puede ser 'A', 'B' o null

const showWithVShow = ref(true);

const showTemplateContent = ref(false);

func on toggleVisibility() {

if ([Link] === 'A') {

[Link] = 'B';

[Link] = false;

[Link] = true;

} else if ([Link] === 'B') {

[Link] = null; // Para ac var el v-else

[Link] = true;

[Link] = false;

} else {

[Link] = 'A';

[Link] = true;

[Link] = false;

}
</script>

Renderizado de Listas (v-for)

La direc va v-for se u liza para renderizar una lista de elementos basándose en una colección de
datos (arrays, objetos o rangos numéricos)

. Es crucial usar el atributo :key con un valor único y estable para cada elemento en la lista. Esto
ayuda a Vue a rastrear la iden dad de cada nodo del DOM, lo que es fundamental para op mizar
el rendimiento al reu lizar, reordenar o actualizar elementos de manera eficiente

Ejemplo de Renderizado de Listas con Composi on API:

<template>

<div>

<h2>Lista de Tareas</h2>

<ul>

<li v-for="task in tasks" :key="[Link]">

{{ [Link] }} (Completada: {{ [Link] ? 'Sí' : 'No' }})

<bu on @click="toggleTaskComple on([Link])">Alternar</bu on>

</li>

</ul>

<h2>Elementos de un Objeto (userInfo)</h2>

<ul>

<li v-for="(value, key) in userInfo" :key="key">

{{ key }}: {{ value }}

</li>

</ul>

<h2>Contador numérico</h2>

<span v-for="n in 5" :key="n">{{ n }} </span>

</div>

</template>
<script setup>

import { ref, reac ve } from 'vue';

const tasks = ref([

{ id: 1, text: 'Aprender Vue Composi on API', completed: false },

{ id: 2, text: 'Construir una aplicación', completed: true },

{ id: 3, text: 'Escribir código limpio', completed: false }

]);

const userInfo = reac ve({

name: 'Jane Doe',

age: 30,

city: 'Nueva York'

});

func on toggleTaskComple on(id) {

const task = [Link].find(t => [Link] === id);

if (task) {

[Link] = ![Link];

</script>

Vinculación de Entradas de Formulario (v-model)

La direc va v-model proporciona una forma conveniente de crear vinculaciones bidireccionales en


elementos de formulario, sincronizando automá camente el valor de entrada con el estado de la
aplicación

. En la Composi on API, v-model se usa picamente con referencias reac vas (ref).

Vue también ofrece modificadores de v-model para manejar escenarios comunes de entrada:
• .lazy: Sincroniza el input después del evento change en lugar de input (ú l para inputs de texto
largos o para reducir actualizaciones)

• .number: Convierte la entrada de cadena a número automá camente

• .trim: Elimina espacios en blanco al principio y al final de la entrada

Para componentes de entrada personalizados, v-model se implementa u lizando una prop


modelValue y emi endo un evento update:modelValue

Ejemplo de v-model y sus Modificadores con Composi on API:

<template>

<div>

<h2>Formulario Interac vo</h2>

<label for="name">Nombre:</label>

<input id="name" type="text" [Link]="userName" placeholder="Tu nombre" />

<p>Hola, {{ userName }}!</p>

<label for="age">Edad:</label>

<input id="age" type="number" [Link]="userAge" />

<p>Tienes {{ userAge }} años.</p>

<label for="message">Mensaje (lazy):</label>

<textarea id="message" [Link]="userMessage"></textarea>

<p>Mensaje final: "{{ userMessage }}"</p>

<label for="subscribe">Suscribirse:</label>

<input id="subscribe" type="checkbox" v-model="isSubscribed" />

<p>Estado de suscripción: {{ isSubscribed ? 'Ac va' : 'Inac va' }}</p>


<h3>Componente de Entrada Personalizado (CustomInput)</h3>

<CustomInput v-model="customValue" label="Valor Personalizado" />

<p>Valor del input personalizado: {{ customValue }}</p>

</div>

</template>

<script setup>

import { ref } from 'vue';

// Asume que este archivo [Link] existe en el mismo directorio o importado


correctamente

import CustomInput from './[Link]';

const userName = ref('');

const userAge = ref(0);

const userMessage = ref('');

const isSubscribed = ref(false);

const customValue = ref('Valor inicial');

</script>

<!-- src/components/[Link] (Ejemplo de implementación) -->

<template>

<div class="custom-input-wrapper">

<label>{{ label }}:</label>

<input

:value="modelValue"

@input="$emit('update:modelValue', $[Link])"

class="custom-input-field"

type="text"

/>
</div>

</template>

<script setup>

import { defineProps, defineEmits } from 'vue';

// defineProps y defineEmits son macros disponibles en <script setup>

defineProps({

modelValue: String,

label: String,

});

defineEmits(['update:modelValue']);

</script>

<style scoped>

.custom-input-wrapper {

margin-bo om: 10px;

.custom-input-field {

padding: 8px;

border: 1px solid #ccc;

border-radius: 4px;

width: 100%;

box-sizing: border-box;

</style>

Vinculación de Clases y Es los (v-bind:class, v-bind:style)


La apariencia de los elementos de la UI a menudo necesita cambiar dinámicamente. Vue
proporciona v-bind:class (o :class) y v-bind:style (o :style) para aplicar clases CSS y es los inline de
forma reac va

• :class:

◦ Sintaxis de Objeto: Permite aplicar clases condicionalmente, donde la clave es el nombre de la


clase y el valor es un booleano (true para aplicar, false para quitar)

◦ Sintaxis de Array: Permite combinar múl ples clases, incluyendo objetos para condiciones

• :style:

◦ Sintaxis de Objeto: Mapea propiedades CSS a valores. Los nombres de propiedades CSS en
camelCase son recomendados

◦ Sintaxis de Array: Combina varios objetos de es lo

Ejemplo de Vinculación de Clases y Es los con Composi on API:

<template>

<div>

<h2>Vinculación Dinámica de Apariencia</h2>

<bu on @click="toggleAc ve">Alternar Ac vo/Error</bu on>

<bu on @click="increaseFontSize">Aumentar/Reiniciar Fuente</bu on>

<div

:class="{ 'highlight': isAc ve, 'error-text': hasError }"

:style="[baseStyles, { fontSize: dynamicFontSize + 'px', color: textColor }]"

class="box"

>

<p>Este texto cambia de color y tamaño.</p>

<p>Ac vo: {{ isAc ve }} | Error: {{ hasError }}</p>


</div>

</div>

</template>

<script setup>

import { ref, reac ve } from 'vue';

const isAc ve = ref(false);

const hasError = ref(false);

const dynamicFontSize = ref(16);

const textColor = ref('black');

const baseStyles = reac ve({

padding: '20px',

border: '1px solid #ccc',

borderRadius: '5px',

margin: '10px'

});

func on toggleAc ve() {

isAc [Link] = !isAc [Link];

[Link] = ![Link];

[Link] = isAc [Link] ? 'blue' : 'red';

func on increaseFontSize() {

[Link] += 2;

if ([Link] > 24) { // Límite para el tamaño de fuente

[Link] = 16;
}

</script>

<style scoped>

.box {

transi on: all 0.3s ease;

min-height: 100px;

display: flex;

flex-direc on: column;

jus fy-content: center;

align-items: center;

text-align: center;

.highlight {

background-color: lightgreen;

border-color: green;

.error-text {

font-weight: bold;

background-color: lightcoral;

border-color: red;

bu on {

margin: 5px;

padding: 8px 15px;

border-radius: 4px;

border: 1px solid #ccc;

background-color: #f0f0f0;
cursor: pointer;

</style>

Construyendo una Interfaz Interac va Completa (Ejemplo)

Combinando todos estos elementos, podemos crear componentes complejos que responden de
manera fluida a las acciones del usuario y a los cambios de datos.

Ejemplo: Un Panel de Filtro de Productos Interac vo

<template>

<div class="product-dashboard">

<h1>Catálogo de Productos</h1>

<div class="controls">

<input

type="text"

[Link]="searchTerm"

placeholder="Buscar por nombre..."

class="search-input"

/>

<select v-model="selectedCategory" class="category-select">

<op on value="all">Todas las categorías</op on>

<op on v-for="category in availableCategories" :key="category" :value="category">

{{ category }}

</op on>

</select>

<bu on @click="toggleShowInStock" :class="{ 'ac ve-filter': showInStock }">

{{ showInStock ? 'Mostrar todos' : 'Mostrar solo en stock' }}

</bu on>

<bu on @click="resetFilters" class="reset-bu on">Resetear Filtros</bu on>

</div>
<p v-if="fi[Link] === 0 && !loading" class="no-results">

No se encontraron productos con los filtros actuales.

</p>

<div v-else-if="loading" class="loading-state">Cargando productos...</div>

<div v-else class="product-grid">

<div

v-for="product in filteredProducts"

:key="[Link]"

:class="['product-card', { 'out-of-stock': [Link] === 0 }]"

>

<h3>{{ [Link] }}</h3>

<p>Categoría: {{ [Link] }}</p>

<p>Precio: ${{ [Link](2) }}</p>

<p :style="{ color: [Link] === 0 ? 'red' : 'green' }">

Stock: {{ [Link] > 0 ? [Link] : 'Agotado' }}

</p>

</div>

</div>

</div>

</template>

<script setup>

import { ref, computed, onMounted } from 'vue';

const allProducts = ref([]);

const searchTerm = ref('');

const selectedCategory = ref('all');

const showInStock = ref(false);


const loading = ref(true);

// Simulación de una llamada a la API

onMounted(async () => {

[Link] = true;

await new Promise(resolve => setTimeout(resolve, 1000)); // Simula un retraso de red

[Link] = [

{ id: 1, name: 'Laptop Pro', category: 'Electrónica', price: 1200, stock: 5 },

{ id: 2, name: 'Smartphone Ultra', category: 'Electrónica', price: 800, stock: 12 },

{ id: 3, name: 'Teclado Mecánico', category: 'Accesorios', price: 150, stock: 0 },

{ id: 4, name: 'Mouse Inalámbrico', category: 'Accesorios', price: 40, stock: 20 },

{ id: 5, name: 'Monitor Curvo', category: 'Electrónica', price: 350, stock: 7 },

{ id: 6, name: 'Webcam HD', category: 'Accesorios', price: 70, stock: 3 }

];

[Link] = false;

});

// Propiedad computada para obtener categorías únicas

const availableCategories = computed(() => {

const categories = new Set([Link](p => [Link]));

return [...categories].sort();

});

// Propiedad computada para filtrar los productos según los criterios

const filteredProducts = computed(() => {

let products = [Link];

if ([Link]) {

products = products.filter(product =>


[Link]().includes([Link]())

);

if ([Link] !== 'all') {

products = products.filter(product => [Link] === [Link]);

if ([Link]) { // Si 'showInStock' es true, filtra los productos con stock > 0

products = products.filter(product => [Link] > 0);

return products;

});

// Función para alternar el filtro de "solo en stock"

func on toggleShowInStock() {

[Link] = ![Link];

// Función para resetear todos los filtros

func on resetFilters() {

[Link] = '';

[Link] = 'all';

[Link] = false;

</script>

<style scoped>
.product-dashboard {

padding: 20px;

font-family: Arial, sans-serif;

background-color: #f9f9f9;

border-radius: 8px;

box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);

h1 {

color: #333;

text-align: center;

margin-bo om: 30px;

.controls {

margin-bo om: 20px;

display: flex;

gap: 10px;

flex-wrap: wrap;

jus fy-content: center;

align-items: center;

.search-input, .category-select, .controls bu on {

padding: 8px 12px;

border: 1px solid #ccc;

border-radius: 5px;

font-size: 1em;

transi on: all 0.2s ease-in-out;

.search-input:focus, .category-select:focus {

border-color: #42b983;
box-shadow: 0 0 0 2px rgba(66, 185, 131, 0.2);

outline: none;

.controls bu on {

background-color: #42b983;

color: white;

cursor: pointer;

border: none;

.controls bu on:hover {

background-color: #368a6e;

.controls bu [Link] ve-filter {

background-color: #007bff;

.controls bu [Link] ve-filter:hover {

background-color: #0056b3;

.controls bu [Link]-bu on {

background-color: #dc3545;

.controls bu [Link]-bu on:hover {

background-color: #c82333;

.loading-state, .no-results {

text-align: center;

font-size: 1.2em;

color: #666;

margin-top: 30px;
}

.product-grid {

display: grid;

grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));

gap: 20px;

.product-card {

border: 1px solid #eee;

border-radius: 8px;

padding: 15px;

box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);

transi on: transform 0.2s;

background-color: white;

.product-card:hover {

transform: translateY(-5px);

box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);

.[Link]-of-stock {

opacity: 0.6;

background-color: #f8f8f8;

cursor: not-allowed;

.product-card h3 {

color: #333;

margin-top: 0;

margin-bo om: 10px;

.product-card p {
margin: 5px 0;

color: #555;

</style>

--------------------------------------------------------------------------------

Imagina que estás en una sala de control de una nave espacial. [Link] es el panel de control
completo. La Composi on API te permite organizar todos los botones, pantallas y luces de forma
lógica: la sección de navegación junta, la de comunicaciones junta, y así. Cada vez que pulsas un
botón (evento) o introduces un dato (v-model), las pantallas (reac vidad) se actualizan
automá camente, algunas se encienden o apagan (v-if/v-show) y los indicadores cambian de color
(v-bind:class/style), todo de manera fluida y coordinada. Puedes incluso ajustar la configuración
para ver diferentes listas de datos (v-for) sin que la interfaz se desordene.

Perfecto
Aquí enes el Capítulo 2: Interfaces Interac vas con [Link] y Composi on API, estructurado con
ejemplos y código documentado paso a paso. He mantenido el es lo didác co y la analogía de la
“sala de control de una nave espacial” para reforzar los conceptos.

Capítulo 2: Interfaces Interac vas (con Composi on API)

Las interfaces interac vas son el núcleo de las aplicaciones web modernas. Con Vue 3 y la
Composi on API, podemos organizar la lógica de interacción de manera coherente, modular y
reac va.

La Reac vidad como Fundamento

La reac vidad en Vue funciona como el sistema de sensores de la nave: cada vez que cambia un
valor, la UI se actualiza automá camente.

Ejemplo básico: contador reac vo

<template>

<div>

<!-- Estado reac vo mostrado en pantalla -->

<p>Contador: {{ count }}</p>

<!-- Propiedad computada que se actualiza automá camente -->


<p>Doble del contador: {{ doubleCount }}</p>

<!-- Botón que dispara un evento -->

<bu on @click="increment">Incrementar</bu on>

</div>

</template>

<script setup>

import { ref, computed } from 'vue';

// Estado reac vo (sensor principal)

const count = ref(0);

// Propiedad computada (pantalla derivada)

const doubleCount = computed(() => [Link] * 2);

// Función que modifica el estado (evento)

func on increment() {

[Link]++;

</script>

Manejo de Eventos (v-on / @)

Los eventos son los botones de la consola de mando. Vue permite escucharlos fácilmente y añadir
modificadores.

Ejemplo con modificadores

<template>

<div>

<!-- Evento click -->


<bu on @click="handleClick">Haz clic</bu on>

<!-- Evento click con propagación detenida -->

<bu on @[Link]="handleClickStopped">Clic detenido</bu on>

<!-- Formulario con prevent default -->

<form @[Link]="handleSubmit">

<input type="text" v-model="formInput" />

<bu on type="submit">Enviar</bu on>

</form>

<p>Input: {{ formInput }}</p>

</div>

</template>

<script setup>

import { ref } from 'vue';

const formInput = ref('');

func on handleClick() {

alert('Botón clicado!');

func on handleClickStopped() {

alert('Clic detenido (no se propagará).');

func on handleSubmit() {
alert(`Formulario enviado con: ${[Link]}`);

</script>

Renderizado Condicional (v-if / v-show)

Aquí controlamos luces que se encienden o apagan en la nave según el estado.

<template>

<div>

<bu on @click="toggleVisibility">Alternar elementos</bu on>

<!-- Renderizado condicional -->

<div v-if="isVisible === 'A'">

<p>Contenido A</p>

</div>

<div v-else-if="isVisible === 'B'">

<p>Contenido B</p>

</div>

<div v-else>

<p>Contenido por defecto</p>

</div>

<!-- v-show (siempre en el DOM, solo oculta con CSS) -->

<p v-show="showWithVShow">Texto con v-show</p>

</div>

</template>

<script setup>

import { ref } from 'vue';


const isVisible = ref('A');

const showWithVShow = ref(true);

func on toggleVisibility() {

if ([Link] === 'A') {

[Link] = 'B';

[Link] = false;

} else {

[Link] = 'A';

[Link] = true;

</script>

Renderizado de Listas (v-for)

Imagina la lista de lecturas de sensores que actualiza datos en pantalla.

<template>

<div>

<h2>Lista de Tareas</h2>

<ul>

<li v-for="task in tasks" :key="[Link]">

{{ [Link] }} (Completada: {{ [Link] ? 'Sí' : 'No' }})

<bu on @click="toggleTaskComple on([Link])">Alternar</bu on>

</li>

</ul>

</div>

</template>

<script setup>
import { ref } from 'vue';

const tasks = ref([

{ id: 1, text: 'Aprender Vue', completed: false },

{ id: 2, text: 'Construir app', completed: true },

]);

func on toggleTaskComple on(id) {

const task = [Link].find(t => [Link] === id);

if (task) [Link] = ![Link];

</script>

v-model en Formularios

El v-model es como un cable bidireccional: la palanca mueve la pantalla, y la pantalla mueve la


palanca.

<template>

<div>

<label>Nombre:</label>

<input [Link]="userName" placeholder="Tu nombre" />

<p>Hola, {{ userName }}!</p>

<label>Edad:</label>

<input type="number" [Link]="userAge" />

<p>Tienes {{ userAge }} años</p>

</div>

</template>

<script setup>
import { ref } from 'vue';

const userName = ref('');

const userAge = ref(0);

</script>

v-bind:class y v-bind:style

Podemos cambiar colores, es los y alertas visuales según los estados.

<template>

<div>

<bu on @click="toggleAc ve">Alternar Estado</bu on>

<div

:class="{ highlight: isAc ve, 'error-text': hasError }"

:style="{ fontSize: dynamicFontSize + 'px' }"

>

Panel interac vo

</div>

</div>

</template>

<script setup>

import { ref } from 'vue';

const isAc ve = ref(false);

const hasError = ref(false);

const dynamicFontSize = ref(16);

func on toggleAc ve() {


isAc [Link] = !isAc [Link];

[Link] = ![Link];

[Link] = isAc [Link] ? 22 : 16;

</script>

<style scoped>

.highlight { background: lightgreen; }

.error-text { color: red; font-weight: bold; }

</style>

Ejemplo Integrador: Panel de Productos

Este ejemplo combina eventos, condicionales, listas, formularios, es los dinámicos y v-model. Es
como el tablero de control completo de la nave.

Ya lo enes arriba en tu texto, lo dejaría como ejemplo final del capítulo.


Formularios y Enlazado (Binding) con Composi on API

Los formularios son la columna vertebral de cualquier aplicación web interac va, permi endo a los
usuarios introducir y manipular datos. En [Link], el enlazado de formularios es un proceso
fundamental que conecta los elementos HTML del formulario con el estado de tu aplicación. Vue
adopta un enfoque declara vo para esta tarea, permi endo a los desarrolladores describir el
resultado deseado y dejando que el framework se encargue de los detalles de implementación

. La Composi on API en Vue 3 ofrece una forma potente y flexible de ges onar la lógica de los
formularios, haciendo que el código sea más organizado, reu lizable y seguro en cuanto a pos,
especialmente en aplicaciones grandes

El Núcleo del Enlazado:

La direc va v-model es la base del enlazado bidireccional en Vue, creando una conexión en empo
real entre un campo de entrada de formulario y el estado de tu componente

. Cuando el usuario escribe en el campo, la propiedad correspondiente en tus datos se actualiza


automá camente, y viceversa. En Vue 3, v-model es una abreviatura para pasar una prop
modelValue y escuchar un evento update:modelValue

Ejemplo Básico de v-model con ref en <script setup>:

<template>

<div>

<label for="nameInput">Nombre:</label>

<input type="text" id="nameInput" v-model="userName" />

<p>Hola, {{ userName }}</p>

</div>

</template>

<script setup lang="ts">

import { ref } from 'vue';

/**

* @property {string} userName - El nombre de usuario enlazado al input.

*/
const userName = ref<string>('');

</script>

<style scoped>

/* Es los básicos para el ejemplo */

div {

padding: 20px;

border: 1px solid #eee;

border-radius: 8px;

max-width: 400px;

margin: 20px auto;

label {

display: block;

margin-bo om: 8px;

font-weight: bold;

input {

width: 100%;

padding: 8px;

margin-bo om: 16px;

border: 1px solid #ccc;

border-radius: 4px;

</style>

En este ejemplo, userName es una referencia reac va creada con ref. Cualquier cambio en el
<input> se refleja instantáneamente en [Link] y, por lo tanto, en el párrafo debajo.

Modificadores de

Vue ofrece modificadores integrados para v-model que te permiten transformar o controlar cómo
se sincroniza la entrada del usuario antes de que se actualice el estado de tu aplicación
. Estos modificadores mejoran la usabilidad y el rendimiento de tus formularios.

1. .lazy: Por defecto, v-model sincroniza la entrada con el estado en cada evento input. El
modificador .lazy cambia esto para que la sincronización ocurra solo en el evento change (cuando
el elemento pierde el foco o el usuario presiona Enter)

. Esto puede ser ú l para campos con validaciones costosas o cuando no se necesita una
actualización en empo real.

2. .number: Si trabajas con entradas numéricas, el valor de un campo input siempre se lee como
una cadena. El modificador .number convierte automá camente el valor de entrada a un número,
evitando conversiones manuales y errores de po

3. .trim: Este modificador recorta automá camente los espacios en blanco de la entrada del
usuario (al principio y al final de la cadena). Es muy ú l para limpiar los datos antes de
almacenarlos o validarlos

Creación de Componentes de Entrada Personalizados

Para construir formularios escalables y mantenibles, es una buena prác ca encapsular los
elementos de entrada con un comportamiento y es lo consistentes en componentes de entrada
personalizados

. Esto reduce la duplicación de código y garan za una experiencia de usuario uniforme.

En Vue 3, implementar v-model en un componente personalizado es directo: el componente hijo


espera una prop modelValue y emite un evento update:modelValue

Ejemplo de un Componente [Link] Personalizado:

<!-- src/components/[Link] -->

<template>

<div class="form-field">

<label v-if="label" :for="id">{{ label }}</label>

<input

:id="id"

:value="modelValue"

@input="handleInput"

:type="type"
:placeholder="placeholder"

:disabled="disabled"

:required="required"

:class="{ 'has-error': errorMessage }"

class="base-input"

/>

<p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>

</div>

</template>

<script setup lang="ts">

import { computed } from 'vue';

// Definimos las props que espera nuestro componente

interface Props {

modelValue: string | number | null; // El valor enlazado del input

label?: string; // E queta del input

type?: string; // Tipo de input (text, email, number, etc.)

placeholder?: string; // Texto de placeholder

id: string; // ID único para el input y la e queta

disabled?: boolean; // Deshabilitar el input

required?: boolean; // Hacer el input requerido

trim?: boolean; // Modificador personalizado .trim

number?: boolean; // Modificador personalizado .number

errorMessage?: string; // Mensaje de error para validación

const props = withDefaults(defineProps<Props>(), {

type: 'text',
modelValue: null,

disabled: false,

required: false,

trim: false,

number: false,

errorMessage: '',

});

// Definimos los eventos que puede emi r nuestro componente

const emit = defineEmits<{

(e: 'update:modelValue', value: string | number | null): void;

}>();

/**

* Maneja el evento 'input' y aplica los modificadores personalizados.

* @param {Event} event - El evento de input.

*/

func on handleInput(event: Event) {

let value: string | number | null = ([Link] as HTMLInputElement).value;

if ([Link] && typeof value === 'string') {

value = [Link]();

if ([Link] && typeof value === 'string' && value !== '') {

value = parseFloat(value);

// Manejar NaN si la conversión falla

if (isNaN(value)) {

value = null;

}
} else if ([Link] && value === '') {

// Si es numérico y está vacío, devolver null para evitar 0 innecesarios

value = null;

emit('update:modelValue', value);

</script>

<style scoped>

/* Es los del componente BaseInput */

.form-field {

margin-bo om: 16px;

label {

display: block;

margin-bo om: 8px;

font-weight: bold;

color: #333;

.base-input {

width: 100%;

padding: 10px 12px;

border: 1px solid #ccc;

border-radius: 6px;

font-size: 1rem;

box-sizing: border-box;

.base-input:focus {
outline: none;

border-color: #007bff;

box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);

.[Link]-error {

border-color: #dc3545;

.error-message {

color: #dc3545;

font-size: 0.875rem;

margin-top: 4px;

</style>

Uso del Componente Personalizado en un Formulario (ej. [Link]):

<!-- src/views/[Link] -->

<template>

<form @[Link]="submitForm" class="user-registra on-form">

<h2>Registro de Usuario</h2>

<BaseInput

id="fullName"

label="Nombre Completo"

type="text"

placeholder="Introduce tu nombre"

[Link]="[Link]"

:required="true"

:error-message="[Link]"

/>
<BaseInput

id="email"

label="Correo Electrónico"

type="email"

placeholder="Introduce tu correo"

[Link]="[Link]"

:required="true"

:error-message="[Link]"

/>

<BaseInput

id="age"

label="Edad"

type="number"

placeholder="Tu edad"

[Link]="[Link]"

:required="true"

:error-message="[Link]"

/>

<bu on type="submit" :disabled="isSubmi ng">

{{ isSubmi ng ? 'Enviando...' : 'Registrar' }}

</bu on>

</form>

</template>

<script setup lang="ts">

import { reac ve, ref, computed } from 'vue';

import BaseInput from '@/components/[Link]'; // Asegúrate de que la ruta sea correcta


// Definición de pos para los datos del formulario

interface UserFormData {

fullName: string;

email: string;

age: number | null;

// Estado del formulario

const formData = reac ve<UserFormData>({

fullName: '',

email: '',

age: null,

});

const isSubmi ng = ref(false);

const errors = reac ve<Record<keyof UserFormData, string>>({

fullName: '',

email: '',

age: '',

});

/**

* Valida los datos del formulario.

* @returns {boolean} - Verdadero si el formulario es válido, falso en caso contrario.

*/

func on validateForm(): boolean {

let isValid = true;

[Link] = '';
[Link] = '';

[Link] = '';

if (![Link]()) {

[Link] = 'El nombre completo es obligatorio.';

isValid = false;

if (![Link]() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test([Link])) {

[Link] = 'Introduce un correo electrónico válido.';

isValid = false;

if ([Link] === null || [Link] <= 0) {

[Link] = 'La edad debe ser un número posi vo.';

isValid = false;

return isValid;

/**

* Maneja el envío del formulario.

*/

async func on submitForm() {

if (!validateForm()) {

alert('Por favor, corrige los errores del formulario.');

return;

isSubmi [Link] = true;


try {

// Simular una llamada a la API

[Link]('Enviando datos:', formData);

await new Promise((resolve) => setTimeout(resolve, 1500));

alert('¡Registro exitoso!');

// Resetear formulario

[Link] = '';

[Link] = '';

[Link] = null;

} catch (error) {

[Link]('Error al registrar usuario:', error);

alert('Hubo un error al registrar. Inténtalo de nuevo.');

} finally {

isSubmi [Link] = false;

</script>

<style scoped>

.user-registra on-form {

padding: 25px;

background-color: #f9f9f9;

border-radius: 10px;

box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);

max-width: 500px;

margin: 30px auto;

h2 {

text-align: center;
color: #333;

margin-bo om: 25px;

font-size: 1.8rem;

bu on {

width: 100%;

padding: 12px 20px;

background-color: #007bff;

color: white;

border: none;

border-radius: 6px;

font-size: 1.1rem;

cursor: pointer;

transi on: background-color 0.3s ease;

margin-top: 15px;

bu on:hover:not(:disabled) {

background-color: #0056b3;

bu on:disabled {

background-color: #cccccc;

cursor: not-allowed;

</style>

Enlazado de Listas y Campos Dinámicos

Las aplicaciones a menudo requieren formularios con un número variable de campos o con pos
de entrada que cambian dinámicamente. Vue proporciona herramientas para manejar estos
escenarios de manera eficiente.

Enlazado de listas (v-for): Cuando necesitas renderizar una lista de elementos de formulario (por
ejemplo, varios campos de contacto), puedes usar v-for en combinación con v-model
.

Campos dinámicos (<component :is="...">): Para formularios que adaptan sus campos basándose
en un esquema de configuración (por ejemplo, cargado desde una API), puedes usar el elemento
<component :is="...">

Ejemplo Simplificado de Formulario Dinámico ([Link]):

<!-- src/components/[Link] -->

<template>

<form @[Link]="handleSubmit">

<div v-for="field in schema" :key="fi[Link]" class="form-field">

<label :for="fi[Link]">{{ fi[Link] }}</label>

<input

v-if="fi[Link] === 'text' || fi[Link] === 'email'"

:type="fi[Link]"

:id="fi[Link]"

v-model="formData[fi[Link]]"

:placeholder="fi[Link]"

:required="fi[Link]"

/>

<select

v-else-if="fi[Link] === 'select'"

:id="fi[Link]"

v-model="formData[fi[Link]]"

:required="fi[Link]"

>

<op on value="" disabled>{{ fi[Link] || 'Selecciona una opción' }}</op on>

<op on v-for="op on in fi[Link] ons" :key="op [Link]" :value="op [Link]">

{{ op [Link] }}

</op on>

</select>
<!-- Otros pos de campo aquí -->

</div>

<bu on type="submit">Enviar</bu on>

</form>

</template>

<script setup lang="ts">

import { reac ve, watch } from 'vue';

// Definición de pos para los campos del esquema

interface FormField {

name: string;

label: string;

type: 'text' | 'email' | 'select';

placeholder?: string;

required?: boolean;

op ons?: { value: string; label: string }[]; // Solo para po 'select'

interface Props {

schema: FormField[];

ini alData?: Record<string, any>;

const props = withDefaults(defineProps<Props>(), {

ini alData: () => ({}),

});

const emit = defineEmits<{


(e: 'submit', formData: Record<string, any>): void;

}>();

// Inicializar formData con ini alData y valores predeterminados del esquema

const formData = reac ve<Record<string, any>>({});

// Función para inicializar/resetear formData

const ini alizeFormData = () => {

[Link]((field) => {

formData[fi[Link]] = [Link] alData[fi[Link]] || '';

if (fi[Link] === 'select' && !formData[fi[Link]]) {

formData[fi[Link]] = ''; // Asegurar que el select tenga un valor inicial vacío si no se


proporciona

});

};

// Inicializar al montar y observar cambios en el esquema o ini alData

ini alizeFormData();

watch(() => [Link], ini alizeFormData, { deep: true });

watch(() => [Link] alData, ini alizeFormData, { deep: true });

/**

* Maneja el envío del formulario dinámico.

*/

func on handleSubmit() {

emit('submit', { ...formData });

</script>
<style scoped>

.form-field {

margin-bo om: 15px;

label {

display: block;

margin-bo om: 5px;

font-weight: bold;

input,

select {

width: 100%;

padding: 8px;

border: 1px solid #ccc;

border-radius: 4px;

bu on {

background-color: #28a745;

color: white;

padding: 10px 15px;

border: none;

border-radius: 4px;

cursor: pointer;

font-size: 1rem;

</style>

Uso del DynamicForm:

<!-- src/views/[Link] -->


<template>

<div class="dynamic-form-example">

<h2>Formulario Dinámico</h2>

<DynamicForm :schema="mySchema" :ini al-data="ini alFormData"


@submit="handleDynamicFormSubmit" />

</div>

</template>

<script setup lang="ts">

import { ref } from 'vue';

import DynamicForm from '@/components/[Link]'; // Ajusta la ruta

const mySchema = ref([

{ name: 'productName', label: 'Nombre del Producto', type: 'text', placeholder: 'Ej: Laptop',
required: true },

{ name: 'category', label: 'Categoría', type: 'select', required: true, op ons: [

{ value: 'electronics', label: 'Electrónica' },

{ value: 'clothing', label: 'Ropa' },

{ value: 'books', label: 'Libros' }

},

{ name: 'price', label: 'Precio', type: 'text', placeholder: 'Ej: 999.99' }, // Podría ser type 'number'
en un BaseInput más avanzado

]);

const ini alFormData = ref({

productName: 'Auriculares Inalámbricos',

category: 'electronics',

});
func on handleDynamicFormSubmit(data: Record<string, any>) {

[Link]('Formulario dinámico enviado:', data);

alert('Formulario dinámico enviado. Revisa la consola.');

</script>

<style scoped>

.dynamic-form-example {

max-width: 600px;

margin: 30px auto;

padding: 25px;

background-color: #f0f8ff;

border-radius: 10px;

box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);

h2 {

text-align: center;

color: #2c3e50;

margin-bo om: 20px;

</style>

Campos Condicionales y Dependientes

En formularios complejos, la visibilidad de ciertos campos puede depender del valor de otros
campos. La Composi on API, junto con v-if y propiedades computed, facilita la implementación de
esta lógica

Ejemplo de Campos Condicionales y Dependientes (con v-if y computed):

<!-- src/views/Condi [Link] -->

<template>

<form @[Link]="submitForm" class="condi onal-form">


<h2>Solicitud de Permiso</h2>

<div class="form-group">

<label for="hasExperience">¿Tienes experiencia previa?</label>

<input type="checkbox" id="hasExperience" v-model="[Link]" />

</div>

<div v-if="[Link]" class="form-group">

<label for="yearsExperience">Años de experiencia:</label>

<input type="number" id="yearsExperience" [Link]="[Link]" />

</div>

<div v-if="showSeniorityField" class="form-group">

<label for="seniorityLevel">Nivel de Seniority:</label>

<select id="seniorityLevel" v-model="[Link]">

<op on value="">Selecciona</op on>

<op on value="junior">Junior</op on>

<op on value="mid">Mid-level</op on>

<op on value="senior">Senior</op on>

</select>

</div>

<bu on type="submit">Enviar Solicitud</bu on>

<div class="debug-output">

<h3>Datos del Formulario:</h3>

<pre>{{ formData }}</pre>

</div>

</form>
</template>

<script setup lang="ts">

import { reac ve, computed } from 'vue';

// Definición de pos para los datos del formulario

interface Applica onFormData {

hasExperience: boolean;

yearsExperience: number | null;

seniorityLevel: string;

const formData = reac ve<Applica onFormData>({

hasExperience: false,

yearsExperience: null,

seniorityLevel: '',

});

/**

* Propiedad computada para controlar la visibilidad del campo 'seniorityLevel'.

* Solo se muestra si ene experiencia y más de 3 años.

*/

const showSeniorityField = computed(() => {

return [Link] && ([Link] || 0) > 3;

});

/**

* Maneja el envío del formulario.

*/
func on submitForm() {

[Link]('Formulario enviado:', formData);

alert('Solicitud enviada. Revisa la consola para ver los datos.');

</script>

<style scoped>

.condi onal-form {

max-width: 550px;

margin: 30px auto;

padding: 25px;

background-color: #e6f7ff;

border-radius: 10px;

box-shadow: 0 2px 10px rgba(0, 0, 0, 0.07);

h2 {

text-align: center;

color: #1890ff;

margin-bo om: 25px;

.form-group {

margin-bo om: 20px;

.form-group label {

display: block;

margin-bo om: 8px;

font-weight: bold;

color: #333;

}
.form-group input[type='checkbox'] {

width: auto;

margin-right: 8px;

transform: scale(1.2); /* Make checkbox slightly larger */

.form-group input[type='number'],

.form-group select {

width: 100%;

padding: 10px 12px;

border: 1px solid #a8d9f1;

border-radius: 6px;

font-size: 1rem;

box-sizing: border-box;

.debug-output {

margin-top: 30px;

padding: 15px;

background-color: #f0f0f0;

border-radius: 8px;

font-family: 'Courier New', Courier, monospace;

font-size: 0.9em;

white-space: pre-wrap;

word-wrap: break-word;

</style>

Envío de Formularios y Manejo de Errores con Composi on API

El envío de formularios en aplicaciones del mundo real implica validar la entrada, manejar
operaciones asíncronas (como llamadas a la API) y proporcionar retroalimentación al usuario. La
Composi on API, con ref, reac ve y funciones async/await, es ideal para ges onar estos flujos

.
Un patrón común es crear un composable de envío de formulario (useFormSubmission) que
encapsule toda la lógica relacionada con la validación, los estados de carga y el manejo de errores,
haciendo que los componentes de formulario sean más limpios y reu lizables

Ejemplo de Composable [Link] (simplificado):

// src/composables/[Link]

import { ref, reac ve } from 'vue';

interface Valida onErrors<T> {

[key: string]: string;

interface FormSubmissionOp ons<T> {

ini alData: T;

validate?: (data: T) => Valida onErrors<T>;

onSubmit: (data: T) => Promise<void>;

/**

* Composable para ges onar el envío de formularios con validación y estados.

* @param {FormSubmissionOp ons<T>} op ons - Opciones de configuración del formulario.

* @returns Un objeto con el estado del formulario y métodos de control.

*/

export func on useFormSubmission<T extends Record<string, any>>(

op ons: FormSubmissionOp ons<T>

){

const formData = reac ve<T>({ ...op [Link] alData });

const valida onErrors = reac ve<Valida onErrors<T>>({});

const isSubmi ng = ref(false);

const submitError = ref<string | null>(null);


const isSuccessful = ref(false);

/**

* Resetea los errores de validación.

*/

const clearValida onErrors = () => {

for (const key in valida onErrors) {

delete valida onErrors[key];

};

/**

* Valida el formulario y actualiza los errores de validación.

* @returns {boolean} - Verdadero si el formulario es válido.

*/

const validate = (): boolean => {

clearValida onErrors();

if (op [Link]) {

const errors = op [Link](formData);

[Link](valida onErrors, errors);

return [Link](errors).length === 0;

return true; // No hay función de validación, se considera válido

};

/**

* Maneja el envío del formulario.

*/

const handleSubmit = async () => {


[Link] = false;

[Link] = null;

if (!validate()) {

return;

isSubmi [Link] = true;

try {

await op [Link](formData);

[Link] = true;

// Opcional: resetear el formulario después de un envío exitoso

// [Link](formData, op [Link] alData);

} catch (error: any) {

[Link] = [Link] || 'Error al enviar el formulario.';

[Link]('Error durante el envío:', error);

} finally {

isSubmi [Link] = false;

};

return {

formData,

valida onErrors,

isSubmi ng,

submitError,

isSuccessful,

handleSubmit,

validate,
clearValida onErrors,

};

Uso del Composable en un Componente de Formulario (ej. [Link]):

<!-- src/views/[Link] -->

<template>

<form @[Link]="handleSubmit" class="contact-form">

<h2>Contáctanos</h2>

<p v-if="submitError" class="global-error-message">{{ submitError }}</p>

<p v-if="isSuccessful" class="success-message">¡Tu mensaje ha sido enviado!</p>

<BaseInput

id="senderName"

label="Tu Nombre"

type="text"

placeholder="Escribe tu nombre"

[Link]="[Link]"

:required="true"

:error-message="valida [Link]"

/>

<BaseInput

id="senderEmail"

label="Tu Correo Electrónico"

type="email"

placeholder="ejemplo@[Link]"

[Link]="[Link]"

:required="true"
:error-message="valida [Link]"

/>

<div class="form-group">

<label for="messageContent">Tu Mensaje</label>

<textarea

id="messageContent"

[Link]="[Link]"

rows="5"

:class="{ 'has-error': valida [Link] }"

required

></textarea>

<p v-if="valida [Link]" class="error-message">{{ valida [Link] }}</p>

</div>

<bu on type="submit" :disabled="isSubmi ng">

{{ isSubmi ng ? 'Enviando...' : 'Enviar Mensaje' }}

</bu on>

</form>

</template>

<script setup lang="ts">

import { reac ve } from 'vue';

import BaseInput from '@/components/[Link]'; // Ajusta la ruta

import { useFormSubmission } from '@/composables/useFormSubmission'; // Ajusta la ruta

// Definición de pos para los datos del formulario de contacto

interface ContactFormData {

name: string;
email: string;

message: string;

// Función de validación para el formulario de contacto

const validateContactForm = (data: ContactFormData) => {

const errors: Record<string, string> = {};

if (![Link]()) {

[Link] = 'El nombre es obligatorio.';

if (![Link]() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test([Link])) {

[Link] = 'Introduce un correo electrónico válido.';

if (![Link]()) {

[Link] = 'El mensaje es obligatorio.';

return errors;

};

// Función de envío a la API (simulada)

const sendContactForm = async (data: ContactFormData) => {

[Link]('Simulando envío a la API:', data);

// Simular un error de API si el mensaje con ene "error"

if ([Link]().includes('error')) {

throw new Error('Mensaje con la palabra "error" detectado. ¡Fallo de envío simulado!');

await new Promise((resolve) => setTimeout(resolve, 2000));

[Link]('Mensaje enviado con éxito:', data);

};
// Usar el composable useFormSubmission

const {

formData,

valida onErrors,

isSubmi ng,

submitError,

isSuccessful,

handleSubmit,

} = useFormSubmission<ContactFormData>({

ini alData: { name: '', email: '', message: '' },

validate: validateContactForm,

onSubmit: sendContactForm,

});

</script>

<style scoped>

.contact-form {

max-width: 600px;

margin: 30px auto;

padding: 30px;

background-color: #fff;

border-radius: 12px;

box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);

h2 {

text-align: center;

color: #2c3e50;

margin-bo om: 30px;


font-size: 2rem;

.global-error-message {

color: #dc3545;

background-color: #f8d7da;

border: 1px solid #f5c6cb;

padding: 10px;

border-radius: 5px;

margin-bo om: 20px;

text-align: center;

.success-message {

color: #28a745;

background-color: #d4edda;

border: 1px solid #c3e6cb;

padding: 10px;

border-radius: 5px;

margin-bo om: 20px;

text-align: center;

.form-group {

margin-bo om: 20px;

.form-group label {

display: block;

margin-bo om: 8px;

font-weight: bold;

color: #333;

}
textarea {

width: 100%;

padding: 10px 12px;

border: 1px solid #ccc;

border-radius: 6px;

font-size: 1rem;

box-sizing: border-box;

resize: ver cal; /* Permite redimensionar ver calmente */

[Link]-error {

border-color: #dc3545;

bu on {

width: 100%;

padding: 14px 20px;

background-color: #007bff;

color: white;

border: none;

border-radius: 8px;

font-size: 1.2rem;

cursor: pointer;

transi on: background-color 0.3s ease;

margin-top: 15px;

bu on:hover:not(:disabled) {

background-color: #0056b3;

bu on:disabled {

background-color: #cccccc;
cursor: not-allowed;

</style>

Mejores Prác cas y Consejos con Composi on API

• Modularización con Composables: Encapsula la lógica de formularios complejos (validación,


envío, manejo de estado específico del formulario) en composables (useForm, useValida on,
useFormSubmission). Esto hace que el código sea más reu lizable y mantenible

• Ges ón de Estado Reac vo: U liza reac ve para objetos de formulario complejos y ref para
valores primi vos individuales. Esto aprovecha el sistema de reac vidad de Vue 3 de manera
eficiente

• Validación de Props en Componentes Personalizados: Al crear componentes de entrada


personalizados, u liza defineProps con interfaces de TypeScript y withDefaults para documentar y
validar las propiedades que el componente acepta, mejorando la seguridad de pos y la
experiencia del desarrollador

• Separación de Preocupaciones: Los componentes deben centrarse en la UI, mientras que la


lógica de negocio y la manipulación de datos deben residir en stores de Pinia o composables

• Accesibilidad (ARIA): Al construir formularios, ten siempre en cuenta la accesibilidad. Usa


e quetas <label> correctamente asociadas con los input mediante los atributos for e id, y
considera los atributos ARIA para roles y estados

Al dominar estas técnicas de enlazado y la Composi on API, estarás bien equipado para construir
formularios reac vos, robustos y fáciles de mantener en tus aplicaciones Vue.

--------------------------------------------------------------------------------

Piensa en el sistema de formularios y enlazado de Vue como una tubería inteligente en tu casa: v-
model es la válvula principal que permite que el agua (tus datos) fluya en ambas direcciones entre
el grifo (el campo de entrada) y el tanque (el estado de tu aplicación). Los modificadores son filtros
o reguladores que ajustan el agua antes de llegar al tanque. Y los componentes personalizados son
grifos de diseño especial que, aunque parezcan diferentes, se conectan a la misma tubería usando
los mismos principios, pero con una apariencia o funcionalidad extra que tú has diseñado.
Perfecto. Te preparé los ejemplos documentados y completos con Composi on API basados en tu
texto. La estructura sigue un progresivo aumento de complejidad: desde un input con v-model,
pasando por modificadores, componentes reu lizables, formularios dinámicos, condicionales y
finalmente el patrón con composables.

Formularios y Enlazado con Composi on API

1. Ejemplo básico de v-model

<template>

<div>

<label for="nameInput">Nombre:</label>

<input type="text" id="nameInput" v-model="userName" />

<p>Hola, {{ userName }}</p>

</div>

</template>

<script setup lang="ts">

import { ref } from 'vue';

/**

* userName es reac vo gracias a ref.

* v-model sincroniza automá camente el valor del input con userName.

*/

const userName = ref<string>('');

</script>

2. Uso de modificadores: .lazy, .trim, .number

<template>

<div>

<label>Email (lazy):</label>
<input type="email" [Link]="email" />

<label>Edad (number):</label>

<input type="number" [Link]="age" />

<label>Usuario (trim):</label>

<input type="text" [Link]="username" />

<pre>{{ { email, age, username } }}</pre>

</div>

</template>

<script setup lang="ts">

import { ref } from 'vue';

const email = ref('');

const age = ref<number | null>(null);

const username = ref('');

</script>

3. Componente reu lizable [Link]

<template>

<div>

<label v-if="label" :for="id">{{ label }}</label>

<input

:id="id"

:value="modelValue"

@input="onInput"

:type="type"
class="input"

/>

<p v-if="errorMessage" class="error">{{ errorMessage }}</p>

</div>

</template>

<script setup lang="ts">

interface Props {

modelValue: string | number | null;

label?: string;

id: string;

type?: string;

errorMessage?: string;

const props = withDefaults(defineProps<Props>(), {

type: 'text',

modelValue: '',

errorMessage: '',

});

const emit = defineEmits<{

(e: 'update:modelValue', value: string | number | null): void;

}>();

/**

* Se emite el evento con el nuevo valor para actualizar el v-model del padre.

*/

func on onInput(e: Event) {


const value = ([Link] as HTMLInputElement).value;

emit('update:modelValue', value);

</script>

4. Uso del BaseInput en un formulario

<template>

<form @[Link]="submitForm">

<BaseInput id="fullName" label="Nombre" [Link]="[Link]" />

<BaseInput id="email" label="Correo" type="email" [Link]="[Link]" />

<BaseInput id="age" label="Edad" type="number" [Link]="[Link]" />

<bu on type="submit">Enviar</bu on>

</form>

</template>

<script setup lang="ts">

import { reac ve } from 'vue';

import BaseInput from '@/components/[Link]';

interface UserForm {

fullName: string;

email: string;

age: number | null;

const formData = reac ve<UserForm>({

fullName: '',

email: '',
age: null,

});

func on submitForm() {

[Link]('Datos enviados:', formData);

</script>

5. Formularios dinámicos con v-for y schema

<template>

<form @[Link]="submitForm">

<div v-for="field in schema" :key="fi[Link]">

<label>{{ fi[Link] }}</label>

<input v-if="fi[Link]==='text'" type="text" v-model="formData[fi[Link]]" />

<select v-else-if="fi[Link]==='select'" v-model="formData[fi[Link]]">

<op on v-for="o in fi[Link] ons" :key="[Link]" :value="[Link]">{{ [Link] }}</op on>

</select>

</div>

<bu on>Guardar</bu on>

</form>

</template>

<script setup lang="ts">

import { reac ve } from 'vue';

const schema = [

{ name: 'product', label: 'Producto', type: 'text' },

{ name: 'category', label: 'Categoría', type: 'select', op ons: [

{ value: 'tech', label: 'Tecnología' },


{ value: 'books', label: 'Libros' },

]},

];

const formData = reac ve<Record<string, any>>({ product: '', category: '' });

func on submitForm() {

[Link]('Formulario dinámico:', formData);

</script>

6. Campos condicionales con computed

<template>

<form @[Link]="submitForm">

<label>

<input type="checkbox" v-model="[Link]" />

¿Tienes experiencia?

</label>

<div v-if="[Link]">

<input type="number" [Link]="[Link]" placeholder="Años de experiencia"


/>

</div>

<div v-if="showSeniority">

<select v-model="[Link]">

<op on value="">Selecciona nivel</op on>

<op on>Junior</op on>

<op on>Senior</op on>


</select>

</div>

<bu on>Enviar</bu on>

</form>

</template>

<script setup lang="ts">

import { reac ve, computed } from 'vue';

const formData = reac ve({

hasExperience: false,

years: 0,

level: '',

});

const showSeniority = computed(() => [Link] && [Link] > 3);

func on submitForm() {

[Link]('Datos:', formData);

</script>

7. Composable useFormSubmission

// src/composables/[Link]

import { ref, reac ve } from 'vue';

export func on useFormSubmission<T extends Record<string, any>>(ini alData: T, validate: (data:


T) => Record<string, string>, onSubmit: (data: T) => Promise<void>) {
const formData = reac ve<T>({ ...ini alData });

const errors = reac ve<Record<string, string>>({});

const isSubmi ng = ref(false);

async func on handleSubmit() {

[Link](errors).forEach(k => delete errors[k]);

const valida on = validate(formData);

[Link](errors, valida on);

if ([Link](errors).length > 0) return;

isSubmi [Link] = true;

try {

await onSubmit(formData);

} finally {

isSubmi [Link] = false;

return { formData, errors, isSubmi ng, handleSubmit };

8. Uso del composable en [Link]

<template>

<form @[Link]="handleSubmit">

<BaseInput id="name" label="Nombre" [Link]="[Link]" :error-


message="[Link]" />

<BaseInput id="email" label="Correo" type="email" [Link]="[Link]" :error-


message="[Link]" />

<textarea [Link]="[Link]"></textarea>
<bu on :disabled="isSubmi ng">Enviar</bu on>

</form>

</template>

<script setup lang="ts">

import BaseInput from '@/components/[Link]';

import { useFormSubmission } from '@/composables/useFormSubmission';

interface ContactForm {

name: string;

email: string;

message: string;

const validate = (d: ContactForm) => {

const e: Record<string, string> = {};

if (![Link]) [Link] = 'El nombre es obligatorio';

if (![Link]('@')) [Link] = 'Correo inválido';

if (![Link]) [Link] = 'Mensaje vacío';

return e;

};

async func on onSubmit(data: ContactForm) {

[Link]('Enviando...', data);

await new Promise(res => setTimeout(res, 1000));

alert('Formulario enviado');

const { formData, errors, isSubmi ng, handleSubmit } = useFormSubmission<ContactForm>(


{ name: '', email: '', message: '' },

validate,

onSubmit

);

</script>
Manejo de Eventos en Vue con la Composi on API

El manejo de eventos es fundamental para construir aplicaciones Vue interac vas, permi endo
que tu aplicación responda a las acciones del usuario y cree experiencias dinámicas

. Vue simplifica la ges ón de eventos del DOM mediante bindings declara vos y potentes
modificadores que reducen el código repe vo. Con la Composi on API de Vue 3, la lógica de
manejo de eventos se integra de manera fluida y organizada dentro de la función setup o
directamente en <script setup>, lo que permite una mayor claridad y reu lización del código

1. Enlazado Básico de Eventos ( / )

Vue ofrece una forma directa de manejar eventos del DOM u lizando la direc va v-on, o su atajo,
el símbolo @

. Para enlazar un evento a una función definida en tu <script setup>, simplemente especificas el
nombre del evento seguido de la función que deseas ejecutar.

Ejemplo Básico: Un Contador Simple

Vamos a crear un componente de contador donde los botones incrementan y decrementan un


valor reac vo:

<template>

<div class="counter-component">

<h2>Contador: {{ count }}</h2>

<bu on @click="increment">Incrementar</bu on>

<bu on @click="decrement">Decrementar</bu on>

</div>

</template>

<script setup>

import { ref } from 'vue';

// Define una referencia reac va para el contador

const count = ref(0);

/**
* @descrip on Incrementa el valor del contador en 1.

* Este método se enlaza al evento 'click' de un botón.

*/

func on increment() {

[Link]++; // Accede y modifica el valor de la ref

[Link]('Contador incrementado a:', [Link]);

/**

* @descrip on Decrementa el valor del contador en 1.

* Este método se enlaza al evento 'click' de otro botón.

*/

func on decrement() {

[Link]--; // Accede y modifica el valor de la ref

[Link]('Contador decrementado a:', [Link]);

</script>

<style scoped>

.counter-component {

padding: 20px;

border: 1px solid #e0e0e0;

border-radius: 8px;

text-align: center;

background-color: #f9f9f9;

bu on {

padding: 8px 15px;

margin: 0 5px;
border: none;

border-radius: 4px;

background-color: #42b983;

color: white;

cursor: pointer;

font-size: 1em;

bu on:hover {

background-color: #36a372;

</style>

Explicación:

• ref(0) crea una variable reac va count

• Las funciones increment y decrement se definen directamente en <script setup> y modifican


[Link].

• @click="increment" y @click="decrement" enlazan estas funciones a los respec vos eventos


click de los botones

2. Acceso al Objeto de Evento Na vo

Vue pasa el objeto Event na vo del DOM como primer argumento a tu manejador de eventos si no
especificas ningún parámetro al llamar a la función en la plan lla

. Esto es ú l cuando necesitas interactuar directamente con el evento, como prevenir su


comportamiento predeterminado.

Ejemplo: Inspeccionar un Evento de Clic

<template>

<div class="event-inspector">

<bu on @click="handleGenericClick">Haz clic aquí</bu on>

<p>Revisa la consola para ver los detalles del evento.</p>

</div>
</template>

<script setup>

/**

* @descrip on Maneja un evento de clic genérico, registrando el objeto de evento na vo.

* @param {MouseEvent} event - El objeto de evento de ratón na vo.

*/

func on handleGenericClick(event) {

[Link]('Evento na vo recibido:', event);

[Link]('Tipo de evento:', [Link]);

[Link]('Elemento obje vo:', [Link]);

[Link]('Coordenadas del clic (x, y):', [Link], [Link]);

</script>

<style scoped>

.event-inspector {

padding: 20px;

border: 1px solid #e0e0e0;

border-radius: 8px;

margin-top: 20px;

background-color: #f9f9f9;

</style>

3. Modificadores de Eventos

Vue proporciona modificadores de eventos para simplificar tareas comunes y evitar la necesidad
de llamar a [Link]() o [Link] on() dentro de tus métodos. Estos se
denotan con un punto (.) después del nombre del evento en la plan lla

Modificadores Comunes:
• .stop: Llama a [Link] on(). De ene la propagación del evento

• .prevent: Llama a [Link](). Previene el comportamiento predeterminado del


evento

• .capture: El evento se dispara en modo de captura.

• .self: Solo dispara el manejador si el evento fue despachado desde el elemento mismo (no de un
hijo)

• .once: El manejador de eventos se disparará como máximo una vez.

• .passive: Indica que el manejador de eventos nunca llamará a preventDefault(). Ú l para mejorar
el rendimiento del desplazamiento en móviles.

Ejemplo de Modificadores de Eventos:

<template>

<div class="modifiers-example">

<h3>Modificadores de Eventos</h3>

<!-- .prevent para formularios -->

<form @[Link]="handleSubmit">

<p>Este formulario no recargará la página.</p>

<bu on type="submit">Enviar Formulario</bu on>

</form>

<div @click="handleParentClick" class="parent-div">

<p>Contenedor Padre (Haz clic aquí o en el botón hijo)</p>

<!-- .stop para detener la propagación -->

<bu on @[Link]="handleChildClick">Botón Hijo ([Link])</bu on>

</div>

<!-- .once para eventos de una sola vez -->


<bu on @[Link]="handleOnceClick">Haz clic una sola vez</bu on>

<!-- .self para solo el elemento propio -->

<div @[Link]="handleSelfClick" class="self-div">

Haz clic aquí (Solo en el div, no en el span)

<span>Este es un span dentro del div</span>

</div>

</div>

</template>

<script setup>

/**

* @descrip on Maneja el envío de un formulario, con el comportamiento predeterminado


prevenido.

*/

func on handleSubmit() {

[Link]('Formulario enviado: comportamiento predeterminado prevenido por .prevent');

alert('Formulario enviado (revisa la consola)');

/**

* @descrip on Maneja un clic en el elemento padre.

* @param {MouseEvent} event - El objeto de evento de ratón.

*/

func on handleParentClick(event) {

[Link]('Clic en el elemento padre. Target:', [Link]);

/**
* @descrip on Maneja un clic en el elemento hijo, deteniendo la propagación.

*/

func on handleChildClick() {

[Link]('Clic en el botón hijo: la propagación del clic se de ene en este elemento.');

alert('Clic en el botón hijo (revisa la consola)');

/**

* @descrip on Maneja un clic que solo se ejecutará una vez.

*/

func on handleOnceClick() {

[Link]('Este botón solo puede ser clicado una vez.');

alert('¡Solo puedes hacer clic aquí una vez!');

/**

* @descrip on Maneja un clic solo cuando el evento ocurre en el propio div, no en sus hijos.

*/

func on handleSelfClick() {

[Link]('Clic en el div mismo, no en el span hijo.');

alert('Clic en el div padre, no en el span.');

</script>

<style scoped>

.modifiers-example {

padding: 20px;

border: 1px solid #e0e0e0;

border-radius: 8px;
margin-top: 20px;

background-color: #f9f9f9;

form, .parent-div, .self-div {

border: 1px dashed #ccc;

padding: 10px;

margin-bo om: 10px;

.parent-div {

background-color: #e6ffe6;

.self-div {

background-color: #e6f7ff;

.self-div span {

background-color: #cceeff;

display: block;

padding: 5px;

margin-top: 5px;

</style>

Encadenamiento de Modificadores: Puedes encadenar múl ples modificadores

: @[Link]="doSomething"

4. Modificadores de Teclado

Para eventos de teclado (keyup, keydown), Vue proporciona modificadores específicos para las
teclas más comunes o combinaciones de teclas

• Teclas Comunes: .enter, .tab, .delete (para Delete y Backspace), .esc, .space, .up, .down, .le ,
.right

.
• Teclas de Sistema: .ctrl, .alt, .shi , .meta (comando en Mac, Windows en Windows)

• .exact: Permite disparar el evento solo cuando la combinación exacta de teclas de sistema es
presionada

. Por ejemplo, @[Link] solo se ac vará si se presiona Enter sin Alt, Ctrl, Shi o Meta.

Ejemplo de Modificadores de Teclado:

<template>

<div class="keyboard-modifiers-example">

<h3>Modificadores de Teclado</h3>

<input

type="text"

@[Link]="handleEnter"

placeholder="Presiona Enter"

class="input-field"

/>

<p>Mensaje: {{ message }}</p>

<input

type="text"

@[Link]="handleSave"

placeholder="Presiona Ctrl+S (previene el guardado del navegador)"

class="input-field"

/>

</div>

</template>

<script setup>

import { ref } from 'vue';


const message = ref('');

/**

* @descrip on Maneja el evento cuando se presiona la tecla Enter en el input.

*/

func on handleEnter() {

[Link] = '¡Enter presionado!';

[Link]('¡Enter presionado!');

/**

* @descrip on Maneja el evento cuando se presiona Ctrl+S, simulando un guardado.

* @param {KeyboardEvent} event - El objeto de evento de teclado na vo.

*/

func on handleSave(event) {

// [Link]() ya es llamado por el modificador .prevent

[Link] = '¡Guardado simulado con Ctrl+S!';

[Link]('¡Guardado simulado!');

</script>

<style scoped>

.keyboard-modifiers-example {

padding: 20px;

border: 1px solid #e0e0e0;

border-radius: 8px;

margin-top: 20px;

background-color: #f9f9f9;

}
.input-field {

padding: 8px;

border: 1px solid #ccc;

border-radius: 4px;

width: 100%;

box-sizing: border-box;

margin-bo om: 10px;

</style>

5. Modificadores de Botones del Ratón

Estos modificadores restringen el manejador de eventos para que solo se ac ve con un botón
específico del ratón

• .le : Solo clic izquierdo.

• .right: Solo clic derecho.

• .middle: Solo clic de la rueda del ratón (botón central).

Ejemplo de Modificadores de Ratón:

<template>

<div class="mouse-modifiers-example">

<h3>Modificadores de Botones del Ratón</h3>

<div @[Link]="handleRightClick" class="click-area">

Haz clic derecho aquí (previene el menú contextual del navegador)

</div>

<div @[Link] ="handleLe Click" class="click-area">

Haz clic izquierdo aquí

</div>

<div @[Link]="handleMiddleClick" class="click-area">

Haz clic con la rueda del ratón aquí

</div>
<p>Úl mo evento: {{ lastMouseEvent }}</p>

</div>

</template>

<script setup>

import { ref } from 'vue';

const lastMouseEvent = ref('Ninguno');

/**

* @descrip on Maneja un clic derecho, previniendo el menú contextual del navegador.

*/

func on handleRightClick() {

[Link] = 'Clic derecho';

[Link]('Clic derecho detectado.');

alert('Clic derecho detectado!');

/**

* @descrip on Maneja un clic izquierdo.

*/

func on handleLe Click() {

[Link] = 'Clic izquierdo';

[Link]('Clic izquierdo detectado.');

/**

* @descrip on Maneja un clic con el botón central del ratón.

*/
func on handleMiddleClick() {

[Link] = 'Clic central';

[Link]('Clic central detectado.');

</script>

<style scoped>

.mouse-modifiers-example {

padding: 20px;

border: 1px solid #e0e0e0;

border-radius: 8px;

margin-top: 20px;

background-color: #f9f9f9;

.click-area {

border: 1px solid #5a5a5a;

padding: 15px;

margin-bo om: 10px;

background-color: #e0e0e0;

cursor: pointer;

text-align: center;

</style>

6. Eventos Personalizados y

Los componentes de Vue se comunican entre sí mediante un flujo de datos unidireccional: los
datos fluyen hacia abajo a través de props (propiedades), y los componentes hijos se comunican
hacia arriba con eventos personalizados

. En la Composi on API, u lizas defineEmits dentro de <script setup> para declarar los eventos que
un componente puede emi r.

Componente Hijo (CustomBu [Link]): Emisión de un Evento Personalizado


<template>

<div class="custom-bu on-container">

<bu on @click="triggerAc on">

{{ label }}

</bu on>

</div>

</template>

<script setup>

import { defineProps, defineEmits } from 'vue';

// Define las props que este componente puede recibir

const props = defineProps({

label: {

type: String,

default: 'Hacer Acción'

},

payloadData: {

type: Object,

default: () => ({ message: 'Acción completada' })

});

// Declara los eventos que este componente puede emi r

const emit = defineEmits([

'ac onTriggered', // Un evento simple

'update:modelValue' // Ejemplo para v-model en componentes personalizados

]);
/**

* @descrip on Dispara el evento personalizado 'ac onTriggered' con datos de carga ú l.

*/

func on triggerAc on() {

[Link]('Evento personalizado "ac onTriggered" emi do con:', [Link]);

emit('ac onTriggered', [Link]);

</script>

<style scoped>

.custom-bu on-container {

margin-top: 20px;

bu on {

background-color: #007bff;

color: white;

padding: 10px 20px;

border: none;

border-radius: 5px;

cursor: pointer;

font-size: 1.1em;

bu on:hover {

background-color: #0056b3;

</style>

Componente Padre ([Link]): Escuchando el Evento Personalizado

<template>

<div class="parent-view-container">
<h2>Vista Padre</h2>

<p>Mensaje del hijo: <strong>{{ childMessage }}</strong></p>

<!-- Escucha el evento 'ac onTriggered' del CustomBu on -->

<CustomBu on

label="Ac var desde Hijo"

:payload-data="{ status: 'success', value: 123 }"

@ac onTriggered="handleChildAc on"

/>

</div>

</template>

<script setup>

import { ref } from 'vue';

import CustomBu on from './CustomBu [Link]'; // Importa el componente hijo

const childMessage = ref('Esperando acción del hijo...');

/**

* @descrip on Maneja el evento 'ac onTriggered' emi do por el componente hijo.

* @param {Object} data - La carga ú l de datos enviada por el evento del hijo.

*/

func on handleChildAc on(data) {

[Link] = `¡El hijo ac vó una acción! Estado: ${[Link]}, Valor: ${[Link]}`;

[Link]('Datos recibidos del hijo:', data);

alert(`Acción del hijo: ${[Link](data)}`);

</script>
<style scoped>

.parent-view-container {

padding: 20px;

border: 1px solid #4CAF50;

border-radius: 8px;

margin-top: 20px;

background-color: #e8f5e9;

</style>

Validación de Emits: Al igual que con las props, puedes definir una validación para tus eventos
emi dos, lo que mejora la robustez y la autodocumentación del componente

<script setup>

import { defineEmits } from 'vue';

const emit = defineEmits({

/**

* @descrip on Evento emi do cuando la can dad ha cambiado.

* @param {number} quan ty - La nueva can dad.

* @returns {boolean} True si la can dad es un número posi vo.

*/

'quan ty-changed': (quan ty) => {

if (typeof quan ty === 'number' && quan ty > 0) {

return true;

} else {

[Link]('¡Can dad inválida emi da para quan ty-changed!');

return false;

},
'item-deleted': null // No necesita validación compleja, solo verifica que se emita

});

func on changeQuan ty(newQuan ty) {

emit('quan ty-changed', newQuan ty);

</script>

7. Buenas Prác cas y Consejos Adicionales

• Mantén los manejadores enfocados: Evita lógica compleja directamente en la plan lla. En su
lugar, define funciones en <script setup> que manejen la lógica y mantenlas lo más enfocadas
posible

• Usa modificadores sabiamente: Los modificadores de eventos son una herramienta poderosa
para mantener la limpieza de la plan lla y la lógica de [Link]() o
[Link] on() fuera de tu JavaScript

• Nombres de eventos personalizados: Sigue la convención de kebab-case para los nombres de los
eventos personalizados, como item-selected o form-submi ed

• Cargas ú les (payloads) de eventos: Si necesitas pasar múl ples datos con un evento
personalizado, emite un objeto para mayor claridad

• Evita $parent: Acceder a $parent o $children directamente crea un acoplamiento frágil entre
componentes. Prefiere props y emits para la comunicación entre padre e hijo

. Para una comunicación más allá de las relaciones directas, considera provide/inject o una
solución de ges ón de estado como Pinia

Dominar el manejo de eventos con la Composi on API de Vue te permite construir interfaces de
usuario altamente interac vas y responsivas, manteniendo al mismo empo un código limpio,
modular y fácil de mantener.

--------------------------------------------------------------------------------

Analogía: Imagina el manejo de eventos en Vue como el sistema nervioso de tu cuerpo. Los
botones, entradas de texto y otras interacciones de la interfaz de usuario son como tus sen dos
(tocar, ver, etc.) que detectan un es mulo. Vue (v-on o @) actúa como los nervios, detectando esos
es mulos (clics, pulsaciones de teclas) y transmi endo la señal. Tus funciones en <script setup>
son como tu cerebro, decidiendo cómo reaccionar a cada señal (incrementar un contador, enviar
un formulario). Los modificadores (.prevent, .stop) son como reflejos automá cos que tu cuerpo
realiza para simplificar la respuesta (por ejemplo, parpadear si algo se acerca a tus ojos). Y los
eventos personalizados (defineEmits) son como tu boca o tus manos, comunicando una acción o
información a otros (un componente padre). Todo funciona de manera coordinada para una
experiencia fluida e intui va.

Perfecto
Tomando tu texto base, ya estructuraste una guía muy completa sobre manejo de eventos en Vue
con Composi on API. Lo que corresponde ahora es consolidarlo en ejemplos de código listos para
copiar/pegar y bien documentados (como pediste: siempre con Composi on API y <script setup>).

Te propongo organizar el material en secciones con ejemplos autocontenidos que puedes probar
en tu proyecto Vue 3:

Manejo de Eventos en Vue con Composi on API

1. Enlazado Básico de Eventos (@click)

Ejemplo: Contador simple

<template>

<div class="counter-component">

<h2>Contador: {{ count }}</h2>

<bu on @click="increment">Incrementar</bu on>

<bu on @click="decrement">Decrementar</bu on>

</div>

</template>

<script setup>

import { ref } from 'vue';

// Estado reac vo

const count = ref(0);


/**

* Incrementa el contador en 1

*/

func on increment() {

[Link]++;

[Link]('Contador incrementado a:', [Link]);

/**

* Decrementa el contador en 1

*/

func on decrement() {

[Link]--;

[Link]('Contador decrementado a:', [Link]);

</script>

2. Acceso al Objeto de Evento Na vo

<template>

<div class="event-inspector">

<bu on @click="handleGenericClick">Haz clic aquí</bu on>

<p>Revisa la consola para ver el objeto de evento.</p>

</div>

</template>

<script setup>

/**

* Recibe el objeto na vo MouseEvent

*/
func on handleGenericClick(event) {

[Link]('Evento completo:', event);

[Link]('Tipo:', [Link]);

[Link]('Target:', [Link]);

[Link](`Coordenadas: x=${[Link]}, y=${[Link]}`);

</script>

3. Modificadores de Eventos (.prevent, .stop, .once, .self)

<template>

<div class="modifiers-example">

<form @[Link]="handleSubmit">

<bu on type="submit">Enviar (prevent)</bu on>

</form>

<div @click="handleParentClick" class="parent">

Padre

<bu on @[Link]="handleChildClick">Hijo (stop)</bu on>

</div>

<bu on @[Link]="handleOnceClick">Clic una sola vez</bu on>

<div @[Link]="handleSelfClick" class="self-box">

Haz clic en este div (no en el span)

<span>Span hijo</span>

</div>

</div>

</template>
<script setup>

func on handleSubmit() {

[Link]('Formulario enviado, preventDefault aplicado.');

func on handleParentClick(e) {

[Link]('Clic en el padre. Target:', [Link]);

func on handleChildClick() {

[Link]('Clic en el hijo, propagación detenida.');

func on handleOnceClick() {

[Link]('Este clic se ejecuta solo una vez.');

func on handleSelfClick() {

[Link]('Clic en el div mismo (no en hijo).');

</script>

4. Modificadores de Teclado (.enter, .ctrl, .exact)

<template>

<div>

<input

type="text"

@[Link]="handleEnter"

placeholder="Presiona Enter"
/>

<p>{{ message }}</p>

<input

type="text"

@[Link]="handleSave"

placeholder="Presiona Ctrl+S"

/>

</div>

</template>

<script setup>

import { ref } from 'vue';

const message = ref('');

func on handleEnter() {

[Link] = 'Enter presionado';

func on handleSave() {

[Link] = 'Guardado simulado con Ctrl+S';

</script>

5. Modificadores de Botones del Ratón (.le , .right, .middle)

<template>

<div>

<div @[Link]="handleRightClick">Clic derecho aquí</div>


<div @[Link] ="handleLe Click">Clic izquierdo aquí</div>

<div @[Link]="handleMiddleClick">Clic central aquí</div>

<p>Úl mo evento: {{ lastEvent }}</p>

</div>

</template>

<script setup>

import { ref } from 'vue';

const lastEvent = ref('Ninguno');

func on handleRightClick() {

[Link] = 'Clic derecho';

func on handleLe Click() {

[Link] = 'Clic izquierdo';

func on handleMiddleClick() {

[Link] = 'Clic central';

</script>

6. Eventos Personalizados (con defineEmits)

Hijo (CustomBu [Link])

<template>

<bu on @click="triggerAc on">{{ label }}</bu on>

</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({

label: { type: String, default: 'Hacer acción' },

payloadData: { type: Object, default: () => ({ status: 'ok' }) }

});

const emit = defineEmits(['ac onTriggered']);

func on triggerAc on() {

emit('ac onTriggered', [Link]);

</script>

Padre ([Link])

<template>

<div>

<p>Mensaje del hijo: {{ childMessage }}</p>

<CustomBu on

label="Ac var"

:payload-data="{ status: 'success', value: 42 }"

@ac onTriggered="handleChildAc on"

/>

</div>

</template>

<script setup>

import { ref } from 'vue';

import CustomBu on from './CustomBu [Link]';


const childMessage = ref('Esperando acción...');

func on handleChildAc on(data) {

[Link] = `Acción recibida: ${[Link](data)}`;

</script>

Con estos ejemplos enes todo el flujo completo:

 Manejo básico de eventos

 Acceso al objeto Event

 Uso de modificadores (mouse, teclado, propagación)

 Comunicación entre componentes con eventos personalizados


Aquí enes los ejemplos y el código documentado basados en tu texto, siempre con Composi on
API. He organizado el material en secciones para que tengas un manual prác co de Vue Router
con Composi on API listo para usar:

Manejo de Rutas con Vue Router y la Composi on API

Vue Router es como el sistema ferroviario de tu aplicación:

 Las rutas son las vías,

 Los componentes son las estaciones,

 <router-link> y [Link]() son los boletos que permiten moverse,

 Los guards de navegación son los inspectores que validan acceso.

1. Instalación y Configuración Básica

npm install vue-router@4

#o

yarn add vue-router@4

src/router/[Link]

import { createRouter, createWebHistory } from 'vue-router'

// Importamos componentes de las vistas

import HomeView from '../views/[Link]'

import AboutView from '../views/[Link]'

import ContactView from '../views/[Link]'

const routes = [

{ path: '/', name: 'Home', component: HomeView },

{ path: '/about', name: 'About', component: AboutView },

{ path: '/contact', name: 'Contact', component: ContactView },

{ path: '/:pathMatch(.*)*', redirect: '/' } // Ruta catch-all

]
const router = createRouter({

history: createWebHistory([Link].BASE_URL),

routes

})

export default router

src/[Link]

import { createApp } from 'vue'

import App from './[Link]'

import router from './router'

const app = createApp(App)

[Link](router)

[Link]('#app')

src/[Link]

<template>

<div id="app">

<header>

<nav>

<router-link to="/">Inicio</router-link> |

<router-link to="/about">Acerca de</router-link> |

<router-link to="/contact">Contacto</router-link>

</nav>

</header>

<main>

<router-view />

</main>

<footer>
<p>&copy; 2023 Mi Aplicación Vue</p>

</footer>

</div>

</template>

<script setup lang="ts">

// Aquí no se necesita lógica extra, Vue Router maneja la navegación.

</script>

2. Rutas Dinámicas y Acceso con Composi on API

Definición de ruta dinámica

path: '/user/:id',

name: 'UserProfile',

component: () => import('../views/UserProfi[Link]')

Acceso a parámetros

src/views/UserProfi[Link]

<template>

<div>

<h1>Perfil de Usuario</h1>

<p>ID: {{ userId }}</p>

<p v-if="loading">Cargando...</p>

<p v-else-if="user">Nombre: {{ [Link] }}</p>

<p v-else-if="error">Error: {{ error }}</p>

</div>

</template>

<script setup lang="ts">


import { ref, onMounted, watch } from 'vue'

import { useRoute } from 'vue-router'

const route = useRoute()

const userId = ref([Link])

const user = ref<{ name: string } | null>(null)

const loading = ref(false)

const error = ref<string | null>(null)

async func on fetchUser(id: string | string[]) {

[Link] = true

try {

await new Promise(r => setTimeout(r, 500))

if (id === '123') {

[Link] = { name: 'Alice Smith' }

} else {

throw new Error('Usuario no encontrado')

} catch (e: any) {

[Link] = [Link]

} finally {

[Link] = false

watch(() => [Link], (newId) => fetchUser(newId))

onMounted(() => fetchUser([Link]))

</script>
3. Navegación Programá ca

src/components/Naviga [Link]

<template>

<div>

<bu on @click="goToAbout">Ir a Acerca de</bu on>

<bu on @click="goBack">Volver</bu on>

<bu on @click="goToProduct(101)">Producto 101</bu on>

</div>

</template>

<script setup lang="ts">

import { useRouter } from 'vue-router'

const router = useRouter()

func on goToAbout() {

[Link]({ name: 'About' })

func on goBack() {

[Link]()

func on goToProduct(id: number) {

[Link]({ name: 'ProductDetail', params: { category: 'electronics', productId: id } })

</script>

4. Query Parameters con Composi on API

<script setup lang="ts">


import { useRoute, useRouter, watch, ref } from 'vue-router'

const route = useRoute()

const router = useRouter()

const search = ref('')

const page = ref(1)

watch(() => [Link], (q) => {

[Link] = [Link]?.toString() || ''

[Link] = parseInt([Link]?.toString() || '1')

}, { immediate: true })

func on nextPage() {

[Link]({ query: { keyword: [Link], page: [Link] + 1 } })

</script>

5. Guards de Navegación

src/router/[Link]

[Link]((to, from) => {

const requiresAuth = [Link]

const isAuth = [Link]('isAuthen cated') === 'true'

if (requiresAuth && !isAuth) {

return { name: 'Login' }

})

router.a erEach((to) => {


[Link](`Navegación completada a ${[Link]}`)

})

6. Rutas Anidadas y Layouts

path: '/dashboard',

component: () => import('../layouts/[Link]'),

children: [

{ path: '', name: 'DashboardHome', component: () => import('../views/dashboard/[Link]') },

{ path: 'profile', name: 'DashboardProfile', component: () =>


import('../views/dashboard/Profi[Link]') }

src/layouts/[Link]

<template>

<div>

<aside>

<router-link :to="{ name: 'DashboardHome' }">Inicio</router-link>

<router-link :to="{ name: 'DashboardProfile' }">Perfil</router-link>

</aside>

<main>

<router-view />

</main>

</div>

</template>

Conclusión

Con Vue Router + Composi on API puedes:

 Definir rutas está cas y dinámicas.


 Usar useRoute y useRouter para acceder y controlar navegación.

 Implementar guards para seguridad.

 Organizar en layouts, rutas anidadas y módulos.


Librerías de Componentes UI y la Composi on API en Vue 3

Las librerías de componentes de interfaz de usuario (UI) son fundamentales en el desarrollo web
moderno con [Link], ya que proporcionan elementos pre-es lizados y accesibles que aceleran el
desarrollo y garan zan la consistencia visual

. Con la llegada de Vue 3 y su Composi on API, la forma en que los desarrolladores interactúan
con estos componentes, tanto de terceros como personalizados, ha evolucionado, ofreciendo
mayor flexibilidad, mejor organización del código y un sólido soporte de pos.

Librerías de Componentes UI Populares (Mención General)

El documento de referencia menciona varias librerías de componentes de UI que han ganado


popularidad en el ecosistema de Vue:

• Vue fy: Destaca por implementar Material Design

. Ofrece componentes pre-es lizados y accesibles. También se menciona como un plugin que es
probable que los desarrolladores hayan usado

• Element UI y BootstrapVue: Se las nombra como librerías de UI que surgieron para proporcionar
componentes listos que implementan sistemas de diseño populares, contribuyendo al crecimiento
exponencial del ecosistema de Vue después del lanzamiento de Vue 2

• PrimeVue: Se describe como un conjunto integral de componentes empresariales

• Quasar Framework: Conocida por sus múl ples modos de build, incluyendo aplicaciones móviles
a través de Capacitor o Cordova

Aunque el documento menciona estas librerías, los ejemplos de código específicos de su


integración directa con la Composi on API no se detallan para todas ellas. Sin embargo, su uso en
Vue 3 se realizaría dentro del contexto de componentes creados con la Composi on API. Por
ejemplo, para Vue fy, el documento muestra un fragmento de plan lla

<template>

<v-container>

<v-row>

<v-col cols="12" md="6">

<v-card>
<v-card- tle>{{ [Link] }}</v-card- tle>

<v-card-text>

<v-ra ng :value="[Link] ng" readonly></v-ra ng>

<p>{{ [Link] on }}</p>

</v-card-text>

<v-card-ac ons>

<v-btn color="primary" @click="addToCart">

Add to Cart

<v-icon right>mdi-cart</v-icon>

</v-btn>

</v-card-ac ons>

</v-card>

</v-col>

</v-row>

</v-container>

</template>

Este fragmento de plan lla, aunque no muestra el bloque <script setup>, sería u lizado en un
componente de Vue 3 que emplea la Composi on API para definir product, addToCart y otras
lógicas, aprovechando así la reac vidad y organización que esta API ofrece.

U lización de Librerías de UI en la Composi on API (Ejemplos Prác cos)

La Composi on API se ha conver do en el método preferido para organizar la lógica de los


componentes, especialmente en Vue 3, agrupando el código por preocupaciones lógicas

. Esto se complementa perfectamente con el uso de librerías de UI o la creación de componentes


UI personalizados.

1. Creación de Componentes de Entrada Personalizados con v-model

La Composi on API simplifica la creación de componentes de entrada personalizados que


interactúan con la direc va v-model, encapsulando el es lo, la validación y el comportamiento en
unidades reu lizables

Un ejemplo de un componente de entrada personalizado ([Link]) usando <script setup>


y v-model sería
:

<template>

<input

:value="modelValue"

@input="$emit('update:modelValue', $[Link])"

class="custom-input"

/>

</template>

<script setup>

import { defineProps, defineEmits } from 'vue';

// Define las props esperadas por v-model

defineProps(['modelValue']);

// Define el evento para actualizar v-model

defineEmits(['update:modelValue']);

</script>

Este patrón garan za que los componentes personalizados puedan u lizar v-model de manera
coherente, permi endo un binding bidireccional entre los inputs del formulario y los datos del
componente

2. Integración con Librerías de Validación de Formularios (VeeValidate y Yup)

VeeValidate es una librería de validación diseñada específicamente para Vue, mientras que Yup es
una librería de validación de esquemas que puede usarse junto con VeeValidate o de forma
independiente

. Ambas se integran perfectamente con la Composi on API para crear sistemas de validación
robustos.

Un ejemplo básico de uso de VeeValidate con <script setup>

<template>

<Form @submit="onSubmit" v-slot="{ errors }">


<div class="form-group">

<label for="email">Email</label>

<Field name="email" type="email" :rules="emailRules" />

<ErrorMessage name="email" class="error-message" />

</div>

<div class="form-group">

<label for="password">Password</label>

<Field name="password" type="password" :rules="passwordRules" />

<ErrorMessage name="password" class="error-message" />

</div>

<bu on type="submit" :disabled="[Link](errors).length > 0">Submit</bu on>

</Form>

</template>

<script setup>

import { Form, Field, ErrorMessage } from 'vee-validate';

// Reglas de validación simples definidas como strings o funciones

const emailRules = 'required|email';

const passwordRules = 'required|min:8';

func on onSubmit(values) {

[Link]('Form submi ed with:', values);

</script>

Para escenarios más avanzados con validación condicional o asíncrona, se pueden usar
composables como useField y esquemas computed con Yup dentro de <script setup>

3. Componentes UI Reu lizables y Tipados (Ejemplo de Botón)

La Composi on API junto con TypeScript (<script setup lang="ts">) permite crear componentes UI
fuertemente pados, mejorando la detección de errores y el autocompletado en el IDE
.

Un ejemplo de un componente Bu [Link] pado con Composi on API

<template>

<bu on

:class="['custom-bu on', `size-${size}`, `variant-${variant}`, { disabled }]"

:disabled="disabled"

@click="onClick"

>

<slot></slot>

</bu on>

</template>

<script setup lang="ts">

import { defineProps, withDefaults, defineEmits } from 'vue';

// Definición de pos para las propiedades

type Bu onSize = 'small' | 'medium' | 'large';

type Bu onVariant = 'primary' | 'secondary' | 'outline' | 'text';

interface Bu onProps {

size?: Bu onSize;

variant?: Bu onVariant;

disabled?: boolean;

// Uso de defineProps con pos genéricos y withDefaults para valores por defecto

const props = withDefaults(defineProps<Bu onProps>(), {

size: 'medium',
variant: 'primary',

disabled: false,

});

// Definición de eventos emi dos

const emit = defineEmits<{

(e: 'click', event: MouseEvent): void;

}>();

func on onClick(event: MouseEvent) {

if (![Link]) {

emit('click', event);

</script>

Este ejemplo demuestra cómo defineProps, withDefaults y defineEmits se u lizan en <script


setup> para crear un componente UI (Bu on) con pado robusto y comportamiento bien definido

4. Sistemas de No ficaciones (Composables para UI)

La Composi on API facilita la extracción de la lógica de UI en composables, lo que permite


reu lizar el comportamiento en múl ples componentes. Un ejemplo es un sistema de
no ficaciones toast

// composables/[Link]

import { ref, reac ve } from 'vue';

const toasts = reac ve([]);

let nextId = 0;

export func on useToast() {


const showToast = ({ type, message, dura on = 5000 }) => {

const id = nextId++;

const toast = { id, type, message, visible: true };

[Link](toast);

if (dura on > 0) {

setTimeout(() => {

const index = toasts.findIndex(t => [Link] === id);

if (index !== -1) {

[Link](index, 1);

}, dura on);

};

return {

toasts, // La lista de toasts reac vos

showToast,

};

Este composable (useToast) u liza ref y reac ve para ges onar el estado de las no ficaciones, que
luego pueden ser consumidas en cualquier componente <script setup> para mostrar mensajes de
UI

. El componente ToastNo fica [Link] se encargaría de renderizar visualmente estos toasts

Principios Clave para la Composi on API y Componentes UI

• script setup: La sintaxis recomendada para usar la Composi on API, reduce el boilerplate y
mejora la inferencia de pos

• ref y reac ve: Primi vas para declarar estado reac vo en los componentes y composables
. ref se usa para valores primi vos y objetos/arrays, mientras que reac ve es para objetos/arrays
que necesitan reac vidad profunda

• computed: Para propiedades derivadas que se almacenan en caché y solo se recalculan cuando
sus dependencias cambian, op mizando el rendimiento de la UI

• defineProps y defineEmits: Macros disponibles en <script setup> para declarar props y eventos
emi dos por un componente de forma clara y con soporte de pado

. Esto es crucial para la comunicación entre componentes UI.

• Composables: Funciones reu lizables que encapsulan lógica con estado. Son ideales para extraer
patrones de UI y lógica de negocio que se comparten entre varios componentes, promoviendo la
reu lización y la separación de preocupaciones

• TypeScript: La integración profunda de Vue 3 con TypeScript ofrece verificación de pos en


empo de compilación, mejor autocompletado y código auto-documentado, especialmente valioso
para definir las interfaces de los componentes UI

Conclusión

La Composi on API de Vue 3, combinada con el uso estratégico de librerías de componentes UI y


la creación de componentes personalizados, permite a los desarrolladores construir interfaces de
usuario altamente interac vas, mantenibles y escalables. Este enfoque equilibra la simplicidad con
la potencia, facilita la reu lización de la lógica de la UI a través de composables y mejora la calidad
del código mediante un pado fuerte, sentando las bases para aplicaciones Vue profesionales y
eficientes.

Perfecto, tomé tu texto base y lo conver en una guía prác ca con ejemplos completos y
documentados que muestran cómo usar librerías de componentes UI junto con la Composi on
API en Vue 3. Te lo estructuré en secciones con <script setup> y explicaciones en los comentarios
para que puedas reu lizarlo como material de referencia.

Ejemplos y Código Documentado con Composi on API

1. Uso de Vue fy con Composi on API

<!-- [Link] -->

<template>
<v-container>

<v-row>

<v-col cols="12" md="6">

<v-card>

<v-card- tle>{{ [Link] }}</v-card- tle>

<v-card-text>

<!-- Uso de un componente de Vue fy con props -->

<v-ra ng :value="[Link] ng" readonly></v-ra ng>

<p>{{ [Link] on }}</p>

</v-card-text>

<v-card-ac ons>

<v-btn color="primary" @click="addToCart">

Add to Cart

<v-icon right>mdi-cart</v-icon>

</v-btn>

</v-card-ac ons>

</v-card>

</v-col>

</v-row>

</v-container>

</template>

<script setup>

import { reac ve } from 'vue';

// Estado reac vo con Composi on API

const product = reac ve({

name: 'Auriculares Pro',

ra ng: 4.5,
descrip on: 'Auriculares con cancelación de ruido y sonido de alta fidelidad',

});

func on addToCart() {

[Link](`${[Link]} agregado al carrito`);

</script>

Aquí Vue fy aporta los componentes UI (v-card, v-btn, v-ra ng), mientras que la Composi on
API organiza el estado y la lógica.

2. Componente de Entrada Personalizado con v-model

<!-- [Link] -->

<template>

<input

:value="modelValue"

@input="$emit('update:modelValue', $[Link])"

class="custom-input"

/>

</template>

<script setup>

import { defineProps, defineEmits } from 'vue';

// v-model en Composi on API

defineProps(['modelValue']);

defineEmits(['update:modelValue']);

</script>

Este patrón garan za que el componente sea compa ble con v-model y se pueda usar así:

<CustomInput v-model="username" />


3. Formularios con Validación (VeeValidate + Composi on API)

<!-- [Link] -->

<template>

<Form @submit="onSubmit" v-slot="{ errors }">

<div class="form-group">

<label for="email">Email</label>

<Field name="email" type="email" :rules="emailRules" />

<ErrorMessage name="email" class="error-message" />

</div>

<div class="form-group">

<label for="password">Password</label>

<Field name="password" type="password" :rules="passwordRules" />

<ErrorMessage name="password" class="error-message" />

</div>

<bu on type="submit" :disabled="[Link](errors).length > 0">

Submit

</bu on>

</Form>

</template>

<script setup>

import { Form, Field, ErrorMessage } from 'vee-validate';

// Reglas básicas

const emailRules = 'required|email';

const passwordRules = 'required|min:8';


func on onSubmit(values) {

[Link]('Datos enviados:', values);

</script>

Con Composi on API + VeeValidate la validación es declara va, manteniendo la lógica


encapsulada en el <script setup>.

4. Componente Reu lizable con Tipado (Bu on)

<!-- Bu [Link] -->

<template>

<bu on

:class="['custom-bu on', `size-${size}`, `variant-${variant}`, { disabled }]"

:disabled="disabled"

@click="onClick"

>

<slot></slot>

</bu on>

</template>

<script setup lang="ts">

import { defineProps, withDefaults, defineEmits } from 'vue';

// Tipado de props

type Bu onSize = 'small' | 'medium' | 'large';

type Bu onVariant = 'primary' | 'secondary' | 'outline' | 'text';

interface Bu onProps {

size?: Bu onSize;
variant?: Bu onVariant;

disabled?: boolean;

const props = withDefaults(defineProps<Bu onProps>(), {

size: 'medium',

variant: 'primary',

disabled: false,

});

const emit = defineEmits<{

(e: 'click', event: MouseEvent): void;

}>();

func on onClick(event: MouseEvent) {

if (![Link]) emit('click', event);

</script>

El uso de TypeScript + Composi on API asegura autocompletado y evita errores en la


comunicación de eventos.

5. Sistema de No ficaciones con Composable

// composables/[Link]

import { reac ve } from 'vue';

const toasts = reac ve([]);

let nextId = 0;

export func on useToast() {


func on showToast({ type, message, dura on = 5000 }) {

const id = nextId++;

const toast = { id, type, message };

[Link](toast);

if (dura on > 0) {

setTimeout(() => {

const index = toasts.findIndex(t => [Link] === id);

if (index !== -1) [Link](index, 1);

}, dura on);

return { toasts, showToast };

<!-- ToastNo fica [Link] -->

<template>

<div class="toast-container">

<div v-for="toast in toasts" :key="[Link]" class="toast">

{{ [Link] }}: {{ [Link] }}

</div>

</div>

</template>

<script setup>

import { useToast } from '@/composables/useToast';

const { toasts } = useToast();

</script>
La lógica está en un composable (useToast), y la UI en el componente. Esto permite
reu lización y separación de preocupaciones.

Claves aplicadas en todos los ejemplos:

 script setup → menos boilerplate y mejor inferencia de pos.

 ref, reac ve → reac vidad declara va.

 defineProps, defineEmits → comunicación clara entre componentes.

 Composables → lógica reu lizable y desacoplada.

 TypeScript opcional → autocompletado y seguridad de pos.


Vite: Una Herramienta Moderna para el Desarrollo Vue

Vite es una herramienta de build más reciente y cada vez más popular que ha transformado el
panorama del desarrollo de aplicaciones Vue, ofreciendo una experiencia significa vamente
mejorada en comparación con herramientas tradicionales como Webpack (u lizado por Vue CLI)

. Se ha conver do en la herramienta recomendada para las aplicaciones Vue, especialmente para


Vue 3

Caracterís cas Clave y Ventajas de Vite

Las ventajas principales de Vite giran en torno al rendimiento y la experiencia del desarrollador

• Arranque Rápido del Servidor de Desarrollo: Vite ofrece un arranque de servidor de desarrollo
mucho más rápido

• Hot Module Replacement (HMR): Proporciona recargas rápidas y eficientes de módulos en


caliente, lo que agiliza el ciclo de desarrollo al permi r ver los cambios al instante sin perder el
estado de la aplicación

• Módulos ES Na vos: Durante el desarrollo, Vite u liza módulos ES na vos directamente en el


navegador, eliminando la necesidad de un bundling pesado antes de servir el código. Esto
contribuye a la velocidad del servidor de desarrollo

• Bundles Op mizados para Producción con Rollup: Para las compilaciones de producción, Vite
u liza Rollup, lo que garan za la creación de bundles op mizados, minimizados y con tree-shaking,
reduciendo el tamaño final de la aplicación y mejorando los empos de carga

Modos de Uso e Instalación

[Link] ofrece varios métodos de instalación, y Vite se presenta como la opción preferida para
proyectos modernos y complejos

1. Crear un Nuevo Proyecto Vue con Vite: La forma más directa de empezar un proyecto Vue con
Vite es u lizando el siguiente comando en tu terminal

:
2. Este comando inicializa un nuevo proyecto Vue 3 ya configurado con Vite, listo para empezar a
desarrollar

3. Comparación con CDN y Vue CLI:

◦ Para fines de aprendizaje o proto pos simples, el enfoque de CDN es suficiente y no requiere
ninguna configuración de build

◦ Sin embargo, para aplicaciones de producción o proyectos complejos, configurar


manualmente Vue CLI o usar Vite te brinda más control sobre el proceso de build y permite
personalizar la configuración según tus requisitos

. Vite es visto como una opción "más nueva y cada vez más popular" que ofrece empos de
compilación más rápidos en comparación con Webpack (usado por Vue CLI)

Integración de Vite en el Ecosistema Vue

Vite se integra de manera profunda y eficiente con varias facetas del desarrollo de aplicaciones
Vue:

• Tes ng (Vitest): Vitest se ha conver do en el framework de prueba preferido para las


aplicaciones de Vue 3 que u lizan Vite como herramienta de build

. La configuración de Vitest se realiza en el archivo [Link]fi[Link], donde se habilita un entorno de


emulación del DOM (como happy-dom) y se especifica la inclusión de componentes Vue para
pruebas

• Op mización del Rendimiento: Vite facilita la op mización de las compilaciones para


producción. Por ejemplo, el documento menciona la integración de rollup-plugin-visualizer en el
[Link]fi[Link] para analizar visualmente el tamaño del paquete, lo que ayuda a iden ficar
dependencias grandes y oportunidades de op mización

. También se pueden configurar manualChunks en rollupOp ons para fragmentación personalizada


del código

• Variables de Entorno: Los proyectos Vue con Vite ges onan las variables de entorno a través de
archivos .env. Estas variables son accesibles en los componentes Vue mediante el objeto
[Link] y deben tener el prefijo VITE_ para ser expuestas al código del lado del cliente.
Esto ayuda a prevenir la exposición accidental de información sensible

.
• Desarrollo de PWA: La integración de la funcionalidad de Progressive Web App (PWA) en
proyectos Vue que u lizan Vite se realiza con el plugin vite-plugin-pwa y la biblioteca workbox-
window. La configuración se define en [Link]fi[Link], permi endo establecer el manifiesto de la
aplicación web y configurar estrategias de caché para los service workers

• Renderizado del Lado del Servidor (SSR): Nuxt 3, una plataforma popular para construir si os
web op mizados para SEO y con SSR, se basa en Vite

. Aunque el documento no detalla cómo Vite impulsa directamente el SSR de Nuxt en cada
mención, la estrecha relación entre Nuxt 3 y Vite subraya la capacidad de Vite para soportar
arquitecturas web avanzadas.

• TypeScript: Al crear un nuevo proyecto Vue 3 con npm init vue@latest, se ofrece la opción de
configurar TypeScript. La configuración resultante incluye archivos como [Link]fi[Link] y shims-
[Link] para asegurar la correcta integración de TypeScript con el entorno de Vite y los
componentes Vue

En conclusión, Vite representa un avance significa vo en las herramientas de build para [Link],
ofreciendo una experiencia de desarrollo velocísima y facilitando la creación de aplicaciones
op mizadas y de alto rendimiento. Su adopción está consolidando un ecosistema moderno y
eficiente para el desarrollo web con Vue 3

--------------------------------------------------------------------------------

Analogía: Piensa en Vite como un constructor de casas de alta tecnología. Las herramientas
tradicionales de construcción de casas (como Webpack) requieren que todos los materiales
(código) sean prefabricados y empaquetados en grandes contenedores antes de que se pueda
empezar a construir. Esto toma empo. Vite, en cambio, ene una logís ca tan avanzada que
puede traer los materiales (módulos ES na vos) directamente al si o a medida que se necesitan,
permi endo que la construcción (servidor de desarrollo) comience casi instantáneamente y los
cambios se reflejen al momento. Para la casa final (producción), u liza técnicas modernas y
eficientes para op mizar cada pieza y ensamblarla de la mejor manera (Rollup), asegurando una
casa ligera y robusta.

Perfecto . Te voy a entregar una guía exhaus va y paso a paso de cómo usar Vite en proyectos
Vue 3, incluyendo instalación, configuración, buenas prác cas, recomendaciones y tutoriales de
uso.

Guía Completa de Vite para Vue 3

1. ¿Qué es Vite y por qué usarlo?


Vite es una herramienta moderna de build para frontend que:

 Usa ES Modules na vos en desarrollo.

 Ofrece servidor de desarrollo instantáneo.

 Tiene Hot Module Replacement (HMR) ultrarrápido.

 Genera builds de producción op mizadas con Rollup.

Piensa en Vite como un constructor en empo real, mientras que Webpack (Vue CLI) funciona
más como una fábrica previa.

2. Instalación y creación de un proyecto Vue con Vite

Paso 1: Crear un proyecto

npm create vue@latest

Durante la creación podrás elegir:

 Vue 3 con o sin TypeScript.

 Router y/o Pinia.

 Tes ng con Vitest.

Paso 2: Entrar al proyecto

cd mi-proyecto-vue

Paso 3: Instalar dependencias

npm install

Paso 4: Iniciar servidor de desarrollo

npm run dev

El servidor quedará corriendo en h p://localhost:5173.

3. Estructura del Proyecto con Vite

Al iniciar, tendrás algo así:

mi-proyecto-vue/

│── [Link]

│── [Link]

│── [Link]fi[Link]
│── src/

│ ├── [Link]

│ ├── [Link]

│ └── components/

│── public/

Diferencias con Vue CLI:

 [Link] está en la raíz (control directo del HTML).

 [Link]fi[Link] maneja plugins y opciones de build.

 No hay [Link]fi[Link].

4. Configuración de Vite

El archivo [Link]fi[Link] básico:

import { defineConfig } from 'vite'

import vue from '@vitejs/plugin-vue'

export default defineConfig({

plugins: [vue()],

server: {

port: 5173,

open: true

},

build: {

sourcemap: true

})

 plugins: se añaden integraciones (ej. Vue, PWA, JSX).

 server: opciones para el servidor local.

 build: configuración de compilación final.


5. Uso de Variables de Entorno

Crear archivo .env:

VITE_API_URL=h ps://[Link]

Acceso desde Vue:

[Link]([Link].VITE_API_URL)

Todas las variables deben comenzar con VITE_ para ser accesibles en el cliente.

6. Tes ng con Vitest

Instalación:

npm install -D vitest @vue/test-u ls happy-dom

Config en [Link]fi[Link]:

test: {

environment: 'happy-dom'

Ejecutar pruebas:

npm run test

7. Op mización de Producción

Análisis de bundle

npm install -D rollup-plugin-visualizer

Config en [Link]fi[Link]:

import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({

plugins: [vue(), visualizer()]

})

Fragmentación manual

build: {

rollupOp ons: {
output: {

manualChunks: {

vue: ['vue'],

vendor: ['axios', 'lodash']

8. Progressive Web Apps (PWA)

Instalar plugin:

npm install -D vite-plugin-pwa workbox-window

Config:

import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({

plugins: [vue(), VitePWA({

registerType: 'autoUpdate',

manifest: {

name: "Mi App Vue",

short_name: "AppVue",

start_url: "/",

display: "standalone",

background_color: "#ffffff",

icons: [...]

})]

})
9. Integración con TypeScript

Si usas npm create vue@latest, puedes elegir TypeScript.


Si no, añade:

npm install -D typescript vue-tsc

Y crea tsconfi[Link].
Vite ya está preparado para TypeScript sin configuración extra.

10. SSR y Nuxt 3

Vite soporta Server-Side Rendering.


En el ecosistema Vue, Nuxt 3 ya está basado en Vite.
Esto significa que:

 SSR, SSG e híbridos funcionan con Vite.

 No enes que configurar nada, Nuxt abstrae la complejidad.

11. Buenas prác cas y recomendaciones

Usa aliases para imports limpios en [Link]fi[Link]:

resolve: {

alias: {

'@': '/src'

Aprovecha [Link] en lugar de exponer .env directo.


Usa pnpm en lugar de npm para dependencias grandes → más rápido.
Mantén el proyecto modular para facilitar fragmentación (manualChunks).
Revisa periódicamente el bundle con rollup-plugin-visualizer.
Si la app es pública, considera añadir soporte PWA.
Para proyectos grandes, ac va TypeScript + ESLint + Pre er desde el inicio.

12. Tutorial paso a paso sugerido

1. Hello World con Vite y Vue


Crear proyecto y probar HMR.
2. Formularios y API
Conectar una API usando variables de entorno.

3. Op mización de producción
Analizar bundle, aplicar fragmentación y lazy loading.

4. Tes ng con Vitest


Probar un componente con @vue/test-u ls.

5. Conver r a PWA
Instalar vite-plugin-pwa y registrar service worker.

También podría gustarte