# 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>
```