# Guía Completa: Proyecto Frontend con Vue 3 + Vite ## 📚 Material Didáctico para Programación Web ## Tabla de Contenidos 1. [Configuración Inicial](#1-configuración-inicial) 2. [Hello World](#2-hello-world) 3. [Componentes Básicos](#3-componentes-básicos) 4. [Composición y Props](#4-composición-y-props) 5. [Estado Reactivo y Composition API](#5-estado-reactivo-y-composition-api) --- ## 1. Configuración Inicial ### 📖 Descripción En esta sección aprenderemos a configurar el entorno de desarrollo para trabajar con Vue 3 y Vite. Vite es un build tool moderno que ofrece un inicio rápido y recarga instantánea durante el desarrollo. ### Requisitos Previos - **Node.js** (versión 16 o superior): Entorno de ejecución para JavaScript - **npm o yarn**: Gestores de paquetes para instalar dependencias ### Crear el Proyecto ```bash # Crear proyecto con Vite # Este comando inicializa un nuevo proyecto Vue 3 usando la plantilla oficial npm create vite@latest mi-proyecto-vue -- --template vue # Navegar al directorio del proyecto cd mi-proyecto-vue # Instalar todas las dependencias necesarias # npm install lee el archivo package.json y descarga todas las librerías npm install # Iniciar servidor de desarrollo # Esto levanta un servidor local en http://localhost:5173 npm run dev ``` ## Estructura Inicial del Proyecto mi-proyecto-vue/ ├── node_modules/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── App.vue │ └── main.js ├── index.html ├── package.json └── vite.config.js - package.json: Define las dependencias del proyecto y scripts de ejecución - vite.config.js: Configura el comportamiento de Vite (plugins, rutas, build) - main.js: Archivo donde se monta la aplicación Vue en el DOM - App.vue: Componente principal que contiene la estructura base ## 2. Hello world Crearemos nuestra primera aplicación Vue, entendiendo la estructura básica de un componente de archivo único (SFC - Single File Component). Este tipo de archivo combina HTML, JavaScript y CSS en un solo lugar. ### Limpiando el Proyecto Base. src/App.vue ```html <!-- TEMPLATE: Define la estructura HTML del componente --> <template> <!-- div contenedor principal con id para estilos --> <div id="app"> <!-- Interpolación de texto: {{ }} permite mostrar variables reactivas --> <h1>{{ mensaje }}</h1> <p>{{ descripcion }}</p> </div> </template> <!-- SCRIPT: Lógica del componente usando Composition API --> <script setup> // Importamos ref desde Vue para crear variables reactivas // ref() crea una referencia reactiva que Vue puede observar y actualizar automáticamente import { ref } from 'vue' // Creamos variables reactivas con ref() // .value contiene el valor real, pero en el template no necesitamos .value const mensaje = ref('¡Hola Mundo con Vue 3 + Vite!') const descripcion = ref('Mi primera aplicación Vue') // 💡 Concepto: Reactividad // Si cambiamos mensaje.value = 'Nuevo texto', Vue actualiza automáticamente el DOM </script> <!-- STYLE: Estilos CSS del componente --> <style scoped> /* scoped: Los estilos solo aplican a este componente Sin scoped: Los estilos serían globales */ #app { font-family: Arial, sans-serif; text-align: center; margin-top: 60px; } h1 { color: #42b983; /* Color verde característico de Vue */ } </style> ``` src/main.js ```js // Importamos la función para crear una aplicación Vue import { createApp } from 'vue' // Importamos estilos globales import './style.css' // Importamos el componente raíz import App from './App.vue' // Creamos la aplicación Vue y la montamos en el elemento con id="app" // El flujo es: createApp(componente) → mount(selector DOM) createApp(App).mount('#app') // 💡 Este es el punto de entrada de toda la aplicación // Vue toma control del div#app en index.html y renderiza App.vue ``` ### Ejemplo manipulacion de atributos src/App.vue ```html <template> <div id="app"> <h1>{{ mensaje }}</h1> <p>{{ descripcion }}</p> <h2>{{ users[1].name }}</h2> <div> <ul> <li v-for="product in products" :key="product.id"> {{ product.name }} - {{ product.price }} </li> </ul> </div> </div> </template> <script setup> import { ref } from 'vue' const mensaje = ref('¡Hola Mundo con Vue 3 + Vite!') const descripcion = ref('Mi primera aplicación Vue') const users = [ { "name": "PP" }, { "name": "jose" }, { "name": "Jaime" } ]; const products = [ { id: 1, name: 'Laptop', price: 1200 }, { id: 2, name: 'Mouse', price: 25 }, { id: 3, name: 'Keyboard', price: 75 } ] </script> <style scoped> #app { font-family: Arial, sans-serif; text-align: center; margin-top: 60px; } h1 { color: #42b983; /* Color verde característico de Vue */ } </style> ``` ## 3. Componentes Básicos Los componentes son piezas reutilizables de código que encapsulan HTML, JavaScript y CSS. Son la base de las aplicaciones Vue, permitiendo dividir la UI en partes independientes y manejables. ### Crear un Componente de Tarjeta src/components/Card.vue ```html <template> <!-- Componente visual de tarjeta reutilizable --> <div class="card"> <!-- Mostramos las props recibidas desde el componente padre --> <h3>{{ titulo }}</h3> <p>{{ contenido }}</p> </div> </template> <script setup> // defineProps() es una función especial de <script setup> // Define qué datos puede recibir este componente desde su padre defineProps({ // Prop 'titulo' de tipo String titulo: String, // Prop 'contenido' de tipo String contenido: String }) // 💡 Concepto: Props (Propiedades) // Son datos que fluyen de padre a hijo (one-way data flow) // El hijo NO debe modificar las props directamente </script> <style scoped> /* Estilos específicos del componente Card */ .card { /* Border y layout básico */ border: 1px solid #ddd; border-radius: 8px; padding: 20px; margin: 10px; /* Sombra para efecto de profundidad */ box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* Transición suave para animaciones */ transition: transform 0.2s; } /* Efecto hover: La tarjeta sube al pasar el mouse */ .card:hover { transform: translateY(-5px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); } </style> ``` Usar el Componente src/App.vue ```html <template> <div id="app"> <h1>Mis Tarjetas</h1> <!-- Usamos el componente Card pasándole props :titulo vincula la prop con un valor :contenido vincula la prop con otro valor --> <Card titulo="Tarjeta 1" contenido="Este es el contenido de la primera tarjeta" /> <!-- Reutilizamos el mismo componente con diferentes datos --> <Card titulo="Tarjeta 2" contenido="Este es el contenido de la segunda tarjeta" /> <!-- 💡 Ventaja: Componentes reutilizables Misma estructura, diferentes datos Mantenimiento más fácil y código DRY (Don't Repeat Yourself) --> </div> </template> <script setup> // Importamos el componente Card para poder usarlo import Card from './components/Card.vue' // En <script setup>, los componentes importados están // automáticamente disponibles en el template </script> ``` ## 4. Composición y Props Aprenderemos a crear componentes interactivos que emiten eventos personalizados y utilizan slots para contenido dinámico. Veremos validación de props avanzada y comunicación bidireccional entre componentes. ### Componente con Eventos src/components/Button.vue ```html <template> <button :class="['btn', variant]" @click="handleClick" :disabled="disabled" > <slot>{{ texto }}</slot> </button> </template> <script setup> const props = defineProps({ // Prop texto con valor por defecto texto: { type: String, // Tipo de dato esperado default: 'Click me' // Valor si no se pasa la prop }, // Prop variant con validación personalizada variant: { type: String, default: 'primary', // Validator: función que verifica valores permitidos validator: (value) => ['primary', 'secondary', 'danger'].includes(value) }, // Prop booleana para deshabilitar el botón disabled: { type: Boolean, default: false } }) // emit options that child can send const emit = defineEmits(['childMessage']) // emit envia el mensaje al padre const handleClick = () => { console.log("Mensaje en el contexto del hijo"); emit('childMessage', 'hizo click') } </script> <style scoped> /* Estilos base del botón */ .btn { padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; transition: all 0.3s; /* Transición suave para todos los cambios */ } /* Variantes de color según la prop 'variant' */ .primary { background-color: #42b983; color: white; } .secondary { background-color: #6c757d; color: white; } .danger { background-color: #dc3545; color: white; } /* Efecto hover solo si el botón NO está deshabilitado */ .btn:hover:not(:disabled) { opacity: 0.8; } /* Estilos para estado disabled */ .btn:disabled { opacity: 0.5; cursor: not-allowed; /* Cambia el cursor para indicar que está deshabilitado */ } </style> ``` App.vue ```html <template> <div id="app"> <h1>Mis Tarjetas</h1> <Buttons texto="Send" variant="danger" @childMessage="handleChildMessage" /> <Card titulo="Tarjeta 1" contenido="Este es el contenido de la primera tarjeta" /> <Card titulo="Tarjeta 2" contenido="Este es el contenido de la segunda tarjeta" /> <Card titulo="Construcción de software" contenido="Universidad La Salle" /> </div> </template> <script setup> import Card from './components/Card.vue' import Buttons from './components/Buttons.vue' // Leemos el mensaje del hijo const handleChildMessage = (message) => { console.log("Mensaje en el contexto del padre"); console.log(message); } </script> ``` - Props Avanzadas: -- type: Valida el tipo de dato -- default: Valor predeterminado -- validator: Función de validación personalizada -- required: Si es obligatoria - Emits (Eventos Personalizados): -- Permiten comunicación hijo → padre -- Se declaran con defineEmits() -- Se emiten con emit('nombreEvento', datos) - Slots: -- Permiten inyectar contenido desde el padre -- <slot> actúa como placeholder -- Pueden tener contenido por defecto - Directivas de Clase: -- :class="['clase1', variable]" - Array de clases -- :class="{ activa: condicion }" - Objeto condicional -- Permiten estilos dinámicos ## 5. Estado Reactivo y Composition API La Composition API es el nuevo sistema de Vue 3 para organizar la lógica del componente. Aprenderemos sobre reactividad, propiedades computadas y cómo extraer lógica reutilizable en composables. ### Contador Interactivo src/components/Counter.vue ```html <template> <div class="counter"> <!-- Mostramos el valor reactivo del contador --> <h2>Contador: {{ count }}</h2> <div class="buttons"> <!-- Cada botón emite un evento que ejecuta una función --> <Button variant="danger" @click="decrement">-</Button> <Button variant="secondary" @click="reset">Reset</Button> <Button variant="primary" @click="increment">+</Button> </div> <!-- Mostramos una propiedad computada --> <p>El contador es {{ paridad }}</p> </div> </template> <script setup> // Importamos funciones de reactividad de Vue import { ref, computed } from 'vue' import Button from './Button.vue' // ref() crea una variable reactiva // count.value contiene el valor real // En el template, accedemos directamente como {{ count }} const count = ref(0) // Funciones para modificar el estado // Estas funciones actualizan count.value const increment = () => count.value++ const decrement = () => count.value-- const reset = () => count.value = 0 // computed() crea una propiedad calculada // Se recalcula automáticamente cuando sus dependencias cambian // Es más eficiente que usar métodos porque cachea el resultado const paridad = computed(() => { // Esta función se ejecuta cada vez que count cambia return count.value % 2 === 0 ? 'par' : 'impar' }) // 💡 Ventajas de computed: // - Se cachea: solo recalcula si sus dependencias cambian // - Es reactivo: actualiza el DOM automáticamente // - Es declarativo: describe QUÉ es, no CÓMO calcularlo </script> <style scoped> .counter { text-align: center; padding: 20px; background-color: #f5f5f5; border-radius: 10px; margin: 20px; } .buttons { display: flex; gap: 10px; /* Espacio entre botones */ justify-content: center; /* Centra los botones */ margin: 20px 0; } </style> ``` - ref(): -- Crea variables reactivas -- Se accede con .value en JavaScript -- En template se usa directamente sin .value computed(): -- Propiedades calculadas que dependen de otras -- Se cachean y solo recalculan cuando sus dependencias cambian -- Son de solo lectura por defecto Reactividad: -- Vue "observa" cambios en datos reactivos -- Actualiza automáticamente el DOM -- Sistema eficiente que solo actualiza lo necesario ### Composable Reutilizable src/composables/useCounter.js ```html // Los composables son funciones que encapsulan lógica reutilizable // Convencionalmente empiezan con 'use' import { ref, computed } from 'vue' // Esta función devuelve estado y métodos relacionados con un contador export function useCounter(initialValue = 0) { // Estado local del composable const count = ref(initialValue) // Métodos para manipular el estado const increment = () => count.value++ const decrement = () => count.value-- const reset = () => count.value = initialValue // Propiedad computada const isEven = computed(() => count.value % 2 === 0) // Retornamos todo lo que queremos exponer return { count, // Estado reactivo increment, // Métodos decrement, reset, isEven // Propiedades computadas } } // 💡 Uso en un componente: // const { count, increment, isEven } = useCounter(5) // 🎯 Ventajas de Composables: // 1. Reutilización de lógica entre componentes // 2. Organización del código por funcionalidad // 3. Fácil de testear // 4. Similar a React Hooks pero más flexible ``` Ejemplo de uso del composable: ```html <script setup> import { useCounter } from '@/composables/useCounter' // Usamos el composable const { count, increment, decrement, isEven } = useCounter(10) // Ahora tenemos acceso a toda la lógica del contador // sin tener que reescribirla </script> ```