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 hookMomento de ejecuciónEscenarios comunes
setup()Inicialización del componente, es el primero en ejecutarseDefinir datos reactivos, propiedades computadas
onBeforeMountAntes de que el componente se monte en el DOMPoco usado
onMountedDespués de que el componente se monte en el DOMRealizar solicitudes de red, inicializar librerías de terceros, obtener referencias al DOM
onBeforeUpdateAntes del cambio de datos y la actualización del DOMPoco usado
onUpdatedDespués del cambio de datos y la actualización del DOMOperaciones después de la actualización del DOM
onBeforeUnmountAntes de que el componente se destruyaLimpiar temporizadores, cancelar suscripciones, eliminar listeners de eventos
onUnmountedDespués de que el componente se destruyaConfirmació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

<script setup>
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

// Ruta del archivo: public/posts.json
[
  {
    "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 enpublic/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

<!-- Ruta de archivo: src/views/HomeView.vue -->
<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:

EstadoCuándo apareceRepresentación en la UI
CargandoDespués de enviar la solicitud, antes de recibir la respuestaAnimación de carga, skeleton screen
ÉxitoLos datos se devuelven correctamenteRenderizar el contenido normalmente
ErrorError de red, anomalía del servidorMensaje 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

<!-- Ruta del archivo: src/views/PostView.vue -->
<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