Vue.js es un framework progresivo diseñado para construir interfaces de usuario. A diferencia de otros frameworks más complejos, Vue adopta un enfoque incremental, centrándose principalmente en la capa de vista. Esto lo hace excepcionalmente fácil de aprender e integrar con otras librerías o proyectos ya existentes. Además, con su sistema de componentes de archivo único y un ecosistema robusto, Vue es perfectamente capaz de impulsar aplicaciones de página única (SPA) sofisticadas.
Para más información, puedes visitar el sitio oficial de Vue.js. Puedes incluir Vue rápidamente en tus proyectos a través de un CDN, como se muestra en los siguientes ejemplos.
Tu Primera Aplicación Vue
Para comenzar, veamos un ejemplo básico que muestra cómo inicializar una instancia de Vue y enlazar datos a la interfaz de usuario.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Primera App Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-inicial">
<p>{{ saludoWeb }}</p>
</div>
<script>
var appInicial = new Vue({
el: "#app-inicial",
data: {
saludoWeb: "¡Hola desde mi primera aplicación Vue!"
}
});
</script>
</body>
</html>
Vue implementa el patrón MVVM (Model-View-ViewModel), centrándose en la reactividad, lo que significa que los cambios en tus datos se reflejan automáticamente en el DOM y viceversa.
Directivas Esenciales de Vue
Las directivas son atributos especiales con el prefijo v-. Indican a Vue cómo manipular el DOM.
Enlace de Atributos con v-bind
La directiva v-bind se utiliza para enlazar reactivamente atributos HTML a expresiones de datos. En el siguiente ejemplo, el atributo title se enlaza a la propiedad mensajeTooltip.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Directiva v-bind</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-bind">
<span v-bind:title="mensajeTooltip">
Pasa el ratón por aquí para ver un mensaje dinámico.
</span>
</div>
<script>
var appBind = new Vue({
el: "#app-bind",
data: {
mensajeTooltip: "¡Tooltip generado por Vue!"
}
});
</script>
</body>
</html>
Renderizado Condicional con v-if, v-else-if, v-else
Estas directivas permiten renderizar elementos condicionalmente, basándose en el valor de una expresión booleana.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Directivas Condicionales</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-condicional">
<h2 v-if="nivelAcceso === 'admin'">Bienvenido, Administrador</h2>
<h2 v-else-if="nivelAcceso === 'editor'">Acceso de Editor</h2>
<h2 v-else>Acceso Limitado</h2>
</div>
<script>
var appCondicional = new Vue({
el: "#app-condicional",
data: {
nivelAcceso: "editor" // Prueba con 'admin', 'editor', o cualquier otro valor
}
});
</script>
</body>
</html>
Renderizado de Listas con v-for
La directiva v-for se utiliza para renderizar una lista de elementos basados en un array o un objeto.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Directiva v-for</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-listas">
<h3>Lista de Frutas</h3>
<ul>
<li v-for="fruta in listaFrutas" :key="fruta.id">
{{ fruta.nombre }} (ID: {{ fruta.id }})
</li>
</ul>
<hr>
<h3>Datos de Usuario</h3>
<ul>
<li v-for="(valor, clave) in datosUsuario" :key="clave">
{{ clave }}: {{ valor }}
</li>
</ul>
</div>
<script>
var appListas = new Vue({
el: "#app-listas",
data: {
listaFrutas: [
{ id: 1, nombre: "Manzana" },
{ id: 2, nombre: "Plátano" },
{ id: 3, nombre: "Cereza" }
],
datosUsuario: {
identificador: "USR123",
nombreCompleto: "Juan Pérez",
edad: 30,
ciudad: "Madrid"
}
}
});
</script>
</body>
</html>
Manejo de Eventos y Enlace Bidireccional
Gestión de Eventos con v-on
La directiva v-on se usa para escuchar eventos del DOM y ejecutar código JavaScript cuando ocurren. Por ejemplo, al hacer clic en un botón.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Manejo de Eventos</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-eventos">
<button v-on:click="mostrarAlerta">Haz clic aquí</button>
</div>
<script>
var appEventos = new Vue({
el: "#app-eventos",
data: {
mensajeAccion: "¡Acción ejecutada por Vue!"
},
methods: {
mostrarAlerta: function (evento){
alert(this.mensajeAccion);
}
}
});
</script>
</body>
</html>
Enlace Bidireccional con v-model
La directiva v-model facilita el enlace de datos bidireccional en elementos de formulario, lo que significa que cualquier cambio en el elemento del formulario actualiza automáticamente el estado de los datos de Vue, y viceversa.
Es importante recordar que v-model ignora los atributos value, checked, o selected definidos en el HTML del formulario. La fuente de verdad inicial siempre proviene de la instancia de Vue.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Enlace Bidireccional</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-modelo">
<label for="entradaTexto">Introduce algo:</label>
<input type="text" id="entradaTexto" v-model="textoInput">
<p>Has escrito: <strong>{{ textoInput }}</strong></p>
<hr>
<h3>Selector de Opción</h3>
<select v-model="opcionSeleccionada">
<option disabled value="">Por favor, selecciona uno</option>
<option>Opción Uno</option>
<option>Opción Dos</option>
<option>Opción Tres</option>
</select>
<p>La opción elegida es: <span>{{ opcionSeleccionada }}</span></p>
</div>
<script>
var appModelo = new Vue({
el: "#app-modelo",
data: {
textoInput: "Texto inicial",
opcionSeleccionada: "Opción Uno"
}
});
</script>
</body>
</html>
Componentes en Vue
Los componentes son la espina dorsal de una aplicación Vue, encapsulando su propio marcado, lógica y estilo. Son instancias de Vue reutilizables y modulables que promueven la organización y la escalabilidad del código.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Componentes Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-componentes">
<h3>Mis Habilidades</h3>
<ul>
<elemento-habilidad v-for="habilidad in misHabilidades" :key="habilidad" :nombre-habilidad="habilidad"></elemento-habilidad>
</ul>
</div>
<script>
// Registro global de un componente
Vue.component('elemento-habilidad', {
props: ['nombreHabilidad'],
template: '<li>{{ nombreHabilidad }}</li>'
});
var appComponentes = new Vue({
el: "#app-componentes",
data: {
misHabilidades: ['JavaScript', 'HTML/CSS', 'Vue.js', 'Node.js']
}
});
</script>
</body>
</html>
El Ciclo de Vida de los Componentes Vue
Cada componente Vue pasa por una serie de fases durante su existencia, desde su creación hasta su destrucción. Estas fases se conocen como el "ciclo de vida". Vue proporciona "hooks" (funciones gancho) que te permiten ejecutar código en puntos específicos de este ciclo, como created, mounted, updated y destroyed. Esto es fundamental para gestionar recursos, realizar peticiones de datos o limpiar efectos secundarios.
Comunicación con Servicios Externos: Axios
Axios es una librería popular basada en promesas para realizar peticiones HTTP. Es compatible tanto en navegadores como en Node.js, facilitando la comunicación asíncrona (AJAX) con APIs externas.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Axios y Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app-axios">
<h3 v-if="datosUsuario.nombre">Perfil de Usuario: {{ datosUsuario.nombre }}</h3>
<p v-if="datosUsuario.email">Email: {{ datosUsuario.email }}</p>
<p v-else>Cargando datos del usuario...</p>
</div>
<script>
// Simula un archivo data.json o usa una API de prueba como JSONPlaceholder
// Para que este ejemplo funcione, necesitarás un servidor local que sirva `user_data.json`
// o puedes cambiar la URL a una API pública (ej: 'https://jsonplaceholder.typicode.com/users/1')
// Contenido de `user_data.json`: {"id": 1, "nombre": "Alice", "email": "alice@example.com"}
var urlAPI = "user_data.json"; // Asegúrate de que este archivo exista o cambia a una URL externa.
var appAxios = new Vue({
el: "#app-axios",
data: {
datosUsuario: {}
},
created() { // El hook `created` es ideal para la carga inicial de datos
axios.get(urlAPI)
.then(response => {
this.datosUsuario = response.data;
})
.catch(error => {
console.error("Hubo un error al obtener los datos:", error);
this.datosUsuario = { nombre: "Error al cargar", email: "N/A" };
});
}
});
</script>
</body>
</html>
Propiedades Calculadas (computed)
Las propiedades calculadas son funciones que se utilizan para procesar y presentar datos de manera reactiva. La diferencia clave con los métodos es que las propiedades calculadas almacenan en caché su resultado y solo se recalculan cuando sus dependencias reactivas cambian. Esto optimiza el rendimiento al evitar recálculos innecesarios.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Propiedades Calculadas</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-calculadas">
<h3>Usando un método para la hora: <span style="color: blue;">{{ obtenerHoraMetodo() }}</span></h3>
<h3>Usando una propiedad calculada para la hora: <span style="color: green;">{{ horaActualCalculada }}</span></h3>
<p>Observa cómo la propiedad calculada se actualiza solo cuando es necesario (o si hay una dependencia reactiva), mientras que el método se ejecuta en cada renderizado.</p>
</div>
<script>
var appCalculadas = new Vue({
el: "#app-calculadas",
data: {
// No hay datos reactivos directos aquí para el ejemplo de Date.now()
},
methods: {
obtenerHoraMetodo: function () {
return new Date().toLocaleTimeString();
}
},
computed: {
horaActualCalculada: function () {
// Esta se recalculará solo si sus dependencias reactivas cambian.
// Como Date.now() no es reactivo por sí mismo, se mantiene el valor inicial
// hasta que haya un cambio en la instancia de Vue que fuerce un re-renderizado.
return new Date().toLocaleTimeString();
}
}
});
</script>
</body>
</html>
La ventaja principal de las propiedades calculadas radica en su capacidad de caching, lo que reduce la carga del sistema al reutilizar resultados ya calculados cuando las dependencias no han cambiado.
Slots: Contenido Dinámico y Reutilizable
Los slots permiten crear componentes más genéricos y reutilizables. Funcionan como "espacios" en la plantilla de un componente donde se puede inyectar contenido arbitrario desde el componente padre. Esto posibilita una gran flexibilidad y composability.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Slots en Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-slots">
<card-simple>
<h3 slot="titulo-slot">Mi Tarjeta Personalizada</h3>
<p slot="contenido-slot">Este es el contenido dinámico inyectado en la tarjeta a través de slots.</p>
<ul slot="elementos-slot">
<li v-for="item in elementosLista" :key="item">{{ item }}</li>
</ul>
</card-simple>
</div>
<script>
// Define el componente de tarjeta que usará slots
Vue.component("card-simple",{
template:
'<div style="border: 1px solid #ccc; padding: 15px; margin: 10px; border-radius: 5px;">'+
'<slot name="titulo-slot"><h3>Título por defecto</h3></slot>'+
'<div><slot name="contenido-slot"><p>Contenido por defecto de la tarjeta.</p></slot></div>'+
'<ul><slot name="elementos-slot"></slot></ul>'+
'</div>'
});
var appSlots = new Vue({
el:"#app-slots",
data: {
elementosLista: ['Elemento A', 'Elemento B', 'Elemento C']
}
});
</script>
</body>
</html>
Eventos Personalizados: Comunicación Hijo a Padre
A menudo, un componente hijo necesita comunicar eventos o cambios de datos a su componente padre. Vue aborda esto con eventos personalizados. El componente hijo puede "emitir" un evento usando this.$emit('nombre-evento', datos), y el componente padre puede "escucharlo" usando v-on:nombre-evento="manejarEvento".
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Eventos Personalizados</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app-custom-events">
<h3>Lista de Tareas Pendientes</h3>
<lista-tareas>
<titulo-lista slot="cabecera" :titulo-lista="tituloTareas"></titulo-lista>
<item-tarea
slot="item"
v-for="(tarea, idx) in misTareas"
:key="idx"
:descripcion="tarea"
:indice="idx"
@tarea-eliminada="eliminarTarea(idx)"></item-tarea>
</lista-tareas>
</div>
<script>
// Componente contenedor de la lista de tareas
Vue.component("lista-tareas",{
template:
'<div>'+
'<slot name="cabecera"></slot>'+
'<ul>'+
'<slot name="item"></slot>'+
'</ul>'+
'</div>'
});
// Componente del título de la lista
Vue.component("titulo-lista",{
props: ['tituloLista'],
template: '<h4>{{ tituloLista }}</h4>'
});
// Componente de cada elemento de la tarea
Vue.component("item-tarea",{
props: ['descripcion','indice'],
template: '<li>{{ indice + 1 }}. {{ descripcion }} <button @click="emitirEliminacion">X</button></li>',
methods: {
emitirEliminacion: function(){
this.$emit("tarea-eliminada");
}
}
});
var appCustomEvents = new Vue({
el: "#app-custom-events",
data: {
tituloTareas: "Tareas por hacer",
misTareas: ['Aprender Vue.js', 'Construir una SPA', 'Dominar componentes']
},
methods: {
eliminarTarea: function(index){
alert("Eliminando tarea: " + this.misTareas[index]);
this.misTareas.splice(index, 1);
}
}
});
</script>
</body>
</html>