---
# System prepended metadata

title: 'Guía Completa: Proyecto Frontend con Vue 3 + Vite'

---

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