Vue.js
¿Qué es Vue.js?
Vue.js es un framework progresivo de JavaScript para construir interfaces de usuario. Creado por Evan You, Vue se caracteriza por ser fácil de aprender, flexible y escalable, permitiendo adoptarlo gradualmente en proyectos existentes o usarlo para aplicaciones completas.
¿Para qué sirve Vue.js?
Vue.js es ideal para:
- Interfaces de usuario interactivas y dinámicas.
- Single Page Applications (SPAs) modernas.
- Aplicaciones web progresivas (PWAs).
- Dashboards y paneles de administración.
- E-commerce y aplicaciones empresariales.
- Migración gradual desde jQuery o JavaScript vanilla.
¿Cómo funciona?
Vue utiliza un sistema reactivo que actualiza automáticamente la interfaz cuando cambian los datos. Imagina Vue como un asistente inteligente que observa tus datos y actualiza la pantalla instantáneamente cuando algo cambia, sin que tengas que decirle específicamente qué hacer.
Ejemplo: Componente básico en Vue
Vue combina HTML, CSS y JavaScript en componentes de una sola archivo (SFC):
<!-- TodoApp.vue -->
<template>
<div class="todo-app">
<h1>{{ title }}</h1>
<form @submit.prevent="addTodo">
<input
v-model="newTodo"
placeholder="¿Qué necesitas hacer?"
required
/>
<button type="submit">Agregar</button>
</form>
<ul class="todo-list">
<li
v-for="todo in filteredTodos"
:key="todo.id"
:class="{ completed: todo.completed }"
>
<input
type="checkbox"
v-model="todo.completed"
/>
<span>{{ todo.text }}</span>
<button @click="removeTodo(todo.id)">❌</button>
</li>
</ul>
<div class="filters">
<button
v-for="filter in filters"
:key="filter"
@click="currentFilter = filter"
:class="{ active: currentFilter === filter }"
>
{{ filter }}
</button>
</div>
<p>{{ remainingTodos }} tareas pendientes</p>
</div>
</template>
<script>
export default {
name: 'TodoApp',
data() {
return {
title: 'Mi Lista de Tareas',
newTodo: '',
currentFilter: 'Todas',
filters: ['Todas', 'Pendientes', 'Completadas'],
todos: [
{ id: 1, text: 'Aprender Vue.js', completed: false },
{ id: 2, text: 'Construir una app', completed: true }
]
};
},
computed: {
filteredTodos() {
switch (this.currentFilter) {
case 'Pendientes':
return this.todos.filter(todo => !todo.completed);
case 'Completadas':
return this.todos.filter(todo => todo.completed);
default:
return this.todos;
}
},
remainingTodos() {
return this.todos.filter(todo => !todo.completed).length;
}
},
methods: {
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({
id: Date.now(),
text: this.newTodo.trim(),
completed: false
});
this.newTodo = '';
}
},
removeTodo(id) {
this.todos = this.todos.filter(todo => todo.id !== id);
}
}
};
</script>
<style scoped>
.todo-app {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.todo-list {
list-style: none;
padding: 0;
}
.todo-list li {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
border-bottom: 1px solid #eee;
}
.completed span {
text-decoration: line-through;
opacity: 0.6;
}
.filters button {
margin: 5px;
padding: 5px 10px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
}
.filters button.active {
background: #42b883;
color: white;
}
</style>
Composition API (Vue 3)
Vue 3 introdujo la Composition API para mejor organización del código:
<template>
<div>
<h2>Contador: {{ count }}</h2>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="reset">Reset</button>
<h3>Usuarios</h3>
<div v-if="loading">Cargando...</div>
<ul v-else>
<li v-for="user in users" :key="user.id">
{{ user.name }} - {{ user.email }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
// Estado reactivo
const count = ref(0);
const users = ref([]);
const loading = ref(true);
// Computed properties
const isEven = computed(() => count.value % 2 === 0);
// Métodos
const increment = () => count.value++;
const decrement = () => count.value--;
const reset = () => count.value = 0;
// Ciclo de vida
onMounted(async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
users.value = await response.json();
} catch (error) {
console.error('Error cargando usuarios:', error);
} finally {
loading.value = false;
}
});
</script>
Directivas de Vue
Vue incluye directivas poderosas para manipular el DOM:
<template>
<div>
<!-- Renderizado condicional -->
<p v-if="isLoggedIn">¡Bienvenido, {{ username }}!</p>
<p v-else>Por favor, inicia sesión</p>
<!-- Mostrar/ocultar (mantiene en DOM) -->
<div v-show="showDetails">
Detalles del usuario...
</div>
<!-- Loops -->
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index + 1 }}. {{ item.name }}
</li>
</ul>
<!-- Binding de atributos -->
<img :src="imageUrl" :alt="imageAlt" />
<button :disabled="isLoading">{{ buttonText }}</button>
<!-- Clases dinámicas -->
<div
:class="{
active: isActive,
disabled: isDisabled,
'has-error': hasError
}"
>
Estado dinámico
</div>
<!-- Estilos dinámicos -->
<div :style="{
color: textColor,
fontSize: fontSize + 'px',
backgroundColor: bgColor
}">
Estilos dinámicos
</div>
<!-- Event listeners -->
<button @click="handleClick">Clic simple</button>
<button @click.prevent="handleSubmit">Prevenir default</button>
<input @keyup.enter="handleEnter" />
<!-- Two-way binding -->
<input v-model="message" placeholder="Escribe algo...">
<p>Mensaje: {{ message }}</p>
</div>
</template>
Comunicación entre componentes
Padre a Hijo (Props):
<!-- ParentComponent.vue -->
<template>
<ChildComponent
:title="pageTitle"
:user="currentUser"
:is-admin="userIsAdmin"
/>
</template>
<!-- ChildComponent.vue -->
<script setup>
const props = defineProps({
title: String,
user: Object,
isAdmin: {
type: Boolean,
default: false
}
});
</script>
Hijo a Padre (Events):
<!-- ChildComponent.vue -->
<template>
<button @click="notifyParent">Enviar al padre</button>
</template>
<script setup>
const emit = defineEmits(['user-action', 'data-updated']);
const notifyParent = () => {
emit('user-action', {
action: 'button-click',
timestamp: Date.now()
});
};
</script>
<!-- ParentComponent.vue -->
<template>
<ChildComponent @user-action="handleUserAction" />
</template>
Estado global con Pinia
Para aplicaciones más grandes, Vue usa Pinia para gestión de estado:
// stores/user.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
currentUser: null,
isAuthenticated: false,
preferences: {
theme: 'light',
language: 'es'
}
}),
getters: {
fullName: (state) => {
return state.currentUser ?
`${state.currentUser.firstName} ${state.currentUser.lastName}` :
'Invitado';
},
isAdmin: (state) => {
return state.currentUser?.role === 'admin';
}
},
actions: {
async login(credentials) {
try {
const response = await api.login(credentials);
this.currentUser = response.user;
this.isAuthenticated = true;
} catch (error) {
throw new Error('Error al iniciar sesión');
}
},
logout() {
this.currentUser = null;
this.isAuthenticated = false;
},
updatePreferences(newPreferences) {
this.preferences = { ...this.preferences, ...newPreferences };
}
}
});
<!-- En un componente -->
<template>
<div>
<h1>Hola, {{ userStore.fullName }}</h1>
<button v-if="!userStore.isAuthenticated" @click="showLogin">
Iniciar Sesión
</button>
<button v-else @click="userStore.logout()">
Cerrar Sesión
</button>
</div>
</template>
<script setup>
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
</script>
Características principales
- Reactive System: Actualización automática de la UI cuando cambian los datos.
- Component-Based: Arquitectura basada en componentes reutilizables.
- Single File Components: HTML, CSS y JS en un solo archivo.
- Virtual DOM: Renderizado eficiente con reconciliación mínima.
- Progressive: Se puede adoptar gradualmente en proyectos existentes.
- Flexible: Múltiples formas de escribir componentes (Options API, Composition API).
Ecosistema Vue
// Principales librerías del ecosistema
const vueEcosystem = {
// Routing
'Vue Router': 'Enrutamiento para SPAs',
// Estado global
'Pinia': 'Gestión de estado (reemplaza Vuex)',
// Build tools
'Vite': 'Build tool ultra-rápido',
'Vue CLI': 'Herramienta de línea de comandos',
// UI Libraries
'Vuetify': 'Material Design components',
'Quasar': 'Framework completo con muchos componentes',
'Element Plus': 'Componentes para aplicaciones empresariales',
// Testing
'Vue Test Utils': 'Utilidades para testing de componentes',
// Meta-frameworks
'Nuxt.js': 'Framework full-stack para Vue'
};
¿Cuándo usar Vue.js?
- Proyectos nuevos que necesitan desarrollo rápido.
- Migración gradual desde jQuery o JavaScript vanilla.
- Equipos mixtos con diferentes niveles de experiencia.
- Aplicaciones empresariales que requieren mantenibilidad.
- Prototipado rápido y MVPs.
- Cuando necesitas curva de aprendizaje suave.
Comparación con otros frameworks
| Característica | Vue.js | React | Angular |
|---|---|---|---|
| Curva de aprendizaje | Suave | Media | Pronunciada |
| Tamaño de bundle | Pequeño | Pequeño | Grande |
| Performance | Excelente | Excelente | Muy buena |
| Ecosistema | Completo | Extenso | Completo |
| Adopción empresarial | Alta | Muy alta | Muy alta |
| Flexibilidad | Alta | Alta | Media |
| TypeScript | Soporte nativo | Excelente | Nativo |
Ventajas de Vue.js
✅ Fácil de aprender: Sintaxis intuitiva y documentación excelente. ✅ Flexible: Múltiples formas de organizar y escribir código. ✅ Performance: Virtual DOM optimizado y reactivity eficiente. ✅ Ecosistema maduro: Librerías oficiales y comunidad activa. ✅ Progressive: Se puede integrar gradualmente. ✅ Single File Components: Organización clara y mantenible.
Configuración básica
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia';
const app = createApp(App);
app.use(router);
app.use(createPinia());
app.mount('#app');
Conclusión
Vue.js es el framework perfecto para desarrolladores que buscan productividad sin sacrificar flexibilidad. Su filosofía progresiva y su curva de aprendizaje suave lo hacen ideal tanto para equipos experimentados como para quienes están empezando con frameworks modernos.
Con su sistema reactivo intuitivo, componentes bien organizados y ecosistema maduro, Vue.js te permite construir desde interfaces simples hasta aplicaciones empresariales complejas, manteniendo siempre el código limpio y mantenible.