Ciclo de vida y solicitudes de datos
En este capítulo aprenderás los hooks del ciclo de vida de Vue3 y usarás fetch para cargar datos de blog desde un archivo JSON en el momento adecuado.
¿Qué es el ciclo de vida?
Cada componente de Vue, desde su creación hasta su destrucción, atraviesa una serie de fases, que se denominan «ciclo de vida».
Vue proporciona en cada fase las correspondientesfunciones hook, que te permiten ejecutar código en momentos específicos.
Por ejemplo: si quieres solicitar datos después de que el componente se haya cargado, necesitas usaronMountedeste hook.
Resumen de los hooks del ciclo de vida en Vue3
| Funciones hook | Momento de ejecución | Escenarios comunes |
|---|---|---|
| setup() | Inicialización del componente, es el primero en ejecutarse | Definir datos reactivos, propiedades computadas |
| onBeforeMount | Antes de que el componente se monte en el DOM | Poco usado |
| onMounted | Después de que el componente se monte en el DOM | Realizar solicitudes de red, inicializar librerías de terceros, obtener referencias al DOM |
| onBeforeUpdate | Antes del cambio de datos y la actualización del DOM | Poco usado |
| onUpdated | Después del cambio de datos y la actualización del DOM | Operaciones después de la actualización del DOM |
| onBeforeUnmount | Antes de que el componente se destruya | Limpiar temporizadores, cancelar suscripciones, eliminar listeners de eventos |
| onUnmounted | Después de que el componente se destruya | Confirmación de tareas de limpieza |
Para el proyecto de blog, el que más usamos esonMounted— obtener los datos de los artículos después de que la página cargue.
Los hooks del ciclo de vida deben escribirse
<script setup>dentro, y no se pueden colocar dentro de funciones asíncronas o sentencias condicionales. Vue determina a qué componente pertenecen según el orden de llamada.
Uso básico de onMounted
Ejemplo
import { ref, onMounted } from 'vue'
const message = ref('Cargando...')
// onMounted se ejecuta después de que el componente se monta en la página
onMounted(() => {
// En este punto el DOM ya se ha renderizado, se puede manipular el DOM o hacer solicitudes de forma segura
console.log('El componente se ha montado, ya se puede empezar a cargar los datos')
message.value = '¡Carga de datos completada!'
})
</script>
<template>
<p>{{ message }}</p>
</template>
Un componente puede llamar a onMounted varias veces, y se ejecutarán en el orden en que se registraron.
Cargar datos JSON con fetch
Vamos a poner los datos de los artículos en un archivo JSON y luego cargarlos con fetch en onMounted.
Paso 1: Crear el archivo de datos
Ejemplo
[
{
"id": 1,
"title": "Guía completa de introducción a Vue3",
"summary": "Aprende desde cero la API de composición de Vue3, que cubre conceptos clave como ref, reactive, computed, etc.",
"content": "<h2>¿Por qué aprender Vue3?</h2><p>Vue3 es actualmente uno de los frameworks frontend más populares...</p>",
"category": "Vue",
"date": "2024-05-10"
},
{
"id": 2,
"title": "Explicación detallada de la programación asíncrona en JavaScript",
"summary": "Entiende de una vez Promise, async/await, el bucle de eventos y la cola de microtareas.",
<h2>¿Qué es asíncrono?</h2><p>JS es de un solo hilo...</p>
"category": "JavaScript",
"date": "2024-05-08"
},
{
"id": 3,
Práctica de CSS Grid Layout
Implementa fácilmente diseños receptivos complejos con CSS Grid.
<h2>Introducción a Grid</h2><p>Grid es un sistema de maquetación bidimensional...</p>
"category": "CSS",
"date": "2024-05-05"
}
]
El archivo JSON se coloca en
public/el directorio, Vite lo servirá directamente como archivo estático. En el navegador se puede acceder/posts.jsondirectamente. Los archivos en public no se compilan, se copian tal cual al producto de compilación.
Paso 2: Hacer fetch de datos en el componente
Ejemplo
<script setup>
import { ref, onMounted } from 'vue'
import BlogCard from '../components/BlogCard.vue'
import CategoryFilter from '../components/CategoryFilter.vue'
const articles = ref([]) // array vacío inicial
const isLoading = ref(true) // estado de carga
const error = ref(null) // mensaje de error
// Cargar datos de artículos de forma asíncrona
async function fetchPosts() {
isLoading.value = true
error.value = null
try {
const response = await fetch('/posts.json')
// Comprobar si la respuesta fue exitosa (lanzar un error en 404 o 500)
if (!response.ok) {
throw new Error(`Carga fallida: HTTP ${response.status}`)
}
const data = await response.json()
articles.value = data
} catch (err) {
error.value = err.message
console.error('Error al cargar los datos de los artículos:', err)
} finally {
isLoading.value = false
}
}
// Cargar los datos inmediatamente después del montaje del componente
onMounted(() => {
fetchPosts()
})
</script>
<template>
<div class="home">
<h2>Artículos recientes</h2>
<!-- Estado de carga -->
<p v-if="isLoading" class="status-msg">Cargando, por favor espera...</p>
<!-- Estado de error -->
<p v-else-if="error" class="status-msg error">
Error al cargar: {{ error }}
<button @click="fetchPosts">Reintentar</button>
</p>
<!-- Estado de datos vacíos -->
<p v-else-if="articles.length === 0" class="status-msg">
Aún no hay artículos, estad atentos.
</p>
<!-- Visualización normal de datos -->
<template v-else>
<div class="article-grid">
<BlogCard
v-for="article in articles"
:key="article.id"
:id="article.id"
:title="article.title"
:summary="article.summary"
:date="article.date"
:category="article.category"
/>
</div>
</template>
</div>
</template>
<style scoped>
.status-msg {
text-align: center;
padding: 60px 0;
font-size: 16px;
color: #999;
}
.error { color: #e74c3c; }
.error button {
margin-left: 10px;
padding: 4px 12px;
cursor: pointer;
}
</style>
Manejar los tres estados de una solicitud asíncrona
Cualquier solicitud de datos debe cubrir los siguientes tres estados de UI, de lo contrario la experiencia de usuario será deficiente:
| Estado | Cuándo aparece | Representación en la UI |
|---|---|---|
| Cargando | Después de enviar la solicitud, antes de recibir la respuesta | Animación de carga, skeleton screen |
| Éxito | Los datos se devuelven correctamente | Renderizar el contenido normalmente |
| Error | Error de red, anomalía del servidor | Mensaje de error + botón de reintentar |
También puedes manejar un cuarto estado adicional:Datos vacíos(la solicitud tiene éxito pero devuelve un array vacío), da al usuario un aviso claro en lugar de una página en blanco.
Manos a la obra: cambia la página de detalles para cargar con fetch también
La página de detalles necesita encontrar el artículo correspondiente en el JSON según el ID de la URL.
Puedes reutilizar la lógica de carga de datos: también llama a fetch en la página de detalles y luego filtra.
Ejemplo
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
const route = useRoute()
const id = Number(route.params.id)
const posts = ref([]) // todos los artículos
const isLoading = ref(true)
const error = ref(null)
// Buscar el artículo actual por ID
const article = computed(() => {
return posts.value.find(p => p.id === id)
})
// Cargar los datos de todos los artículos
async function fetchPosts() {
isLoading.value = true
try {
const res = await fetch('/posts.json')
if (!res.ok) throw new Error('Carga fallida')
posts.value = await res.json()
} catch (err) {
error.value = err.message
} finally {
isLoading.value = false
}
}
onMounted(() => {
fetchPosts()
})
</script>
<template>
<div class="post-view">
<p v-if="isLoading">Cargando...</p>
<p v-else-if="error">Error al cargar: {{ error }}</p>
<div v-else-if="!article">
<h2>El artículo no existe</h2>
<RouterLink to="/">Volver al inicio</RouterLink>
</div>
<article v-else>
<span class="category-tag">{{ article.category }}</span>
<h1>{{ article.title }}</h1>
<time>{{ article.date }}</time>
<div class="content" v-html="article.content"></div>
<RouterLink to="/">← Volver al inicio</RouterLink>
</article>
</div>
</template>
Resumen del capítulo
En este capítulo has dominado onMounted, el hook más esencial del ciclo de vida de Vue3, el uso de fetch para cargar datos JSON desde el directorio public, y cómo manejar los cuatro estados de UI de una solicitud (cargando/éxito/error/vacío).
Ahora los datos del blog se cargan de forma asíncrona desde un archivo JSON externo, lo que se acerca más a la forma de trabajar en un proyecto real.
Otras extensiones