Vue.js

Vuejs

Tutorial Vuejs: Introducción a Suspense

Vuejs Introducción a Suspense: Aprende a gestionar componentes y datos asíncronos eficientemente en Vue.js mostrando contenido provisional mientras se resuelven las dependencias.

Aprende Vuejs GRATIS y certifícate

¿Qué es Suspense en Vue.js?

Suspense es un componente incorporado en Vue.js que permite gestionar componentes asíncronos y la obtención de datos que requieren tiempo para resolverse. Introducido en Vue 3, Suspense proporciona una manera elegante de mostrar contenido de respaldo (default) mientras se resuelven las dependencias asíncronas, mejorando así la experiencia del usuario.

Cabe destacar que se trata de una feature experimental, por lo que no podemos garantizar que alcance un estado estable y la API puede tener cambios tras ser completamente estable

En aplicaciones modernas, es común que los componentes dependan de operaciones asíncronas como peticiones a APIs o carga dinámica de datos. Sin Suspense, los desarrolladores debían manejar manualmente los estados de carga y los casos en los que los datos aún no estaban disponibles, lo que complicaba el código y podía generar inconsistencias.

Suspense simplifica este proceso al permitir que los componentes declaren sus dependencias asíncronas y gestionen automáticamente el renderizado cuando todos los recursos están listos. Mientras tanto, los usuarios ven un contenido de reserva que puede ser un indicador de carga u otro componente provisional.

El funcionamiento de Suspense se basa en envolver los componentes asíncronos dentro del componente Suspense. Si los componentes hijos están esperando datos o en proceso de carga, Suspense muestra el fallback especificado hasta que todo esté preparado. Una vez que las dependencias se resuelven, el contenido principal se renderiza automáticamente.

Con Suspense, se facilita el manejo de escenarios asíncronos en Vue.js, permitiendo un código más limpio y una mejor experiencia para el usuario final.

Configuración básica de Suspense

Para utilizar Suspense en una aplicación Vue, es necesario comprender su estructura básica y cómo se integra en el flujo de componentes. El componente Suspense actúa como un contenedor que envuelve componentes asíncronos, permitiendo gestionar de forma sencilla los estados de carga y renderizado.

La sintaxis fundamental de Suspense implica utilizar dos slots nombrados: default y fallback. El slot default contiene el contenido principal que eventualmente se renderizará, mientras que el slot fallback proporciona contenido alternativo que se muestra mientras los componentes hijos están pendientes de resolver sus operaciones asíncronas.

Aquí se presenta un ejemplo básico de cómo implementar Suspense:

<template>
  <Suspense>
    <template #default>
      <!-- Contenido principal que depende de datos asíncronos -->
      <ComponenteAsincrono />
    </template>
    <template #fallback>
      <!-- Contenido que se muestra mientras se cargan los datos -->
      <div>Cargando...</div>
    </template>
  </Suspense>
</template>

En este ejemplo, <ComponenteAsincrono /> representa un componente cuyo contenido requiere una operación asíncrona, como una llamada a una API. Mientras este componente está resolviendo sus datos, el slot fallback mostrará al usuario un mensaje de carga o un indicador de progreso.

Es importante destacar que Suspense se encarga automáticamente de determinar cuándo todos los componentes asíncronos dentro del slot default han completado sus operaciones. Una vez que esto ocurre, el contenido del slot default se renderiza, reemplazando al contenido del slot fallback.

Para que Suspense funcione correctamente, los componentes hijos deben manejar sus operaciones asíncronas mediante el uso de la API de composición de Vue, específicamente utilizando async. Un componente asíncrono básico podría definirse de la siguiente manera:

<script setup lang="ts">
import { ref } from 'vue'

const datos = ref(null)

async function obtenerDatos() {
  // Simulación de una llamada a una API
  datos.value = await fetchDatosDesdeAPI()
}

await obtenerDatos()
</script>

<template>
  <div>
    <!-- Renderizado del contenido una vez que los datos están disponibles -->
    <p>Datos: {{ datos }}</p>
  </div>
</template>

En este componente, la función obtenerDatos() es declarada como async, lo que permite utilizar await para esperar la resolución de las operaciones asíncronas. De esta forma, Suspense puede detectar que el componente está en un estado pendiente y manejar adecuadamente la transición entre los slots fallback y default.

Al configurar Suspense, es recomendable proporcionar una experiencia de usuario fluida al diseñar contenido de reserva que informe claramente al usuario que se está cargando información. Esto puede incluir animaciones, barras de progreso o mensajes personalizados que mejoren la interacción.

De esta manera, Suspense se convierte en una herramienta eficaz para manejar operaciones asíncronas en Vue, simplificando el código y mejorando la experiencia del usuario.

Uso de Suspense con componentes asíncronos

El uso de componentes asíncronos es una técnica eficiente para mejorar el rendimiento de una aplicación Vue, permitiendo cargar componentes sólo cuando son necesarios. Al combinar componentes asíncronos con Suspense, se puede gestionar de forma elegante la carga y presentación de estos componentes, mejorando la experiencia del usuario.

Para definir un componente asíncrono en Vue, se utiliza una función que devuelve una promesa que resuelve el componente. Por ejemplo:

<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

const ComponenteAsincrono = defineAsyncComponent(() =>
  import('./ComponenteAsincrono.vue')
)
</script>

<template>
  <Suspense>
    <template #default>
      <ComponenteAsincrono />
    </template>
    <template #fallback>
      <div>Cargando componente...</div>
    </template>
  </Suspense>
</template>

En este ejemplo, ComponenteAsincrono se carga de forma dinámica cuando es necesario. El componente Suspense envuelve al componente asíncrono y muestra el contenido del slot fallback mientras el componente se carga.

Es posible personalizar aún más el componente asíncrono utilizando opciones adicionales. Por ejemplo, se puede implementar un tiempo de espera o un componente de error en caso de que la carga falle:

<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

const ComponenteAsincrono = defineAsyncComponent({
  loader: () => import('./ComponenteAsincrono.vue'),
  loadingComponent: {
    template: '<div>Cargando...</div>'
  },
  errorComponent: {
    template: '<div>Error al cargar el componente.</div>'
  },
  delay: 2000,
  timeout: 3000,
})
</script>

<template>
  <Suspense>
    <template #default>
      <ComponenteAsincrono />
    </template>
    <template #fallback>
      <div>Cargando componente asíncrono...</div>
    </template>
  </Suspense>
</template>

En este caso, se utilizan las propiedades loadingComponent y errorComponent para especificar componentes que se mostrarán durante la carga o si ocurre un error. Las propiedades delay y timeout controlan el comportamiento del loader, estableciendo un retraso antes de mostrar el componente de carga y un tiempo máximo antes de considerar que ha ocurrido un error.

El uso de Suspense con componentes asíncronos facilita el manejo de varios componentes que pueden tardar en cargarse. Por ejemplo, si se tienen múltiples componentes asíncronos, Suspense esperará a que todos se hayan resuelto antes de renderizar el contenido:

<template>
  <Suspense>
    <template #default>
      <ComponenteUno />
      <ComponenteDos />
    </template>
    <template #fallback>
      <div>Cargando contenido...</div>
    </template>
  </Suspense>
</template>

Aquí, ComponenteUno y ComponenteDos podrían ser componentes asíncronos o dependientes de datos que requieren tiempo para obtenerse. El fallback proporciona una retroalimentación al usuario mientras espera.

Además, es posible anidar varios componentes Suspense para manejar diferentes niveles de carga. Esto permite mostrar diferentes indicadores de carga para distintas partes de la interfaz de usuario:

<template>
  <Suspense>
    <template #default>
      <SeccionPrincipal />
      <Suspense>
        <template #default>
          <SeccionSecundaria />
        </template>
        <template #fallback>
          <div>Cargando sección secundaria...</div>
        </template>
      </Suspense>
    </template>
    <template #fallback>
      <div>Cargando sección principal...</div>
    </template>
  </Suspense>
</template>

En este ejemplo, se proporciona una experiencia más detallada al usuario, mostrando mensajes de carga específicos para cada sección.

Es importante tener en cuenta que los componentes asíncronos y Suspense trabajan de la mano para optimizar la carga y renderización de la aplicación. Mientras que los componentes asíncronos permiten dividir el código y cargar sólo lo necesario, Suspense gestiona de forma eficiente los estados de carga y los presenta de manera coherente al usuario.

Al utilizar Suspense con componentes asíncronos, se mejora la eficiencia y se ofrece una mejor experiencia al usuario, al tiempo que se mantiene un código limpio y mantenible.

Integración de Suspense con fetching de datos

El componente Suspense no sólo es útil para manejar componentes asíncronos, sino que también es una herramienta efectiva para gestionar la obtención de datos desde APIs o fuentes externas. Al integrar Suspense con peticiones de datos, podemos proporcionar una experiencia de usuario fluida mientras esperamos la respuesta de las operaciones asíncronas.

Para utilizar Suspense con datos asíncronos, es necesario que los componentes hijos utilicen una función setup asíncrona o retornen una promesa que Suspense pueda detectar. Esto permite que Suspense sepa cuándo el componente está listo para ser renderizado.

A continuación, se presenta un ejemplo de cómo combinar Suspense con fetching de datos:

<template>
  <Suspense>
    <template #default>
      <DatosUsuario />
    </template>
    <template #fallback>
      <div>Cargando datos del usuario...</div>
    </template>
  </Suspense>
</template>

<script setup lang="ts">
// No es necesario código adicional aquí; la lógica está en el componente hijo
</script>

En este ejemplo, <DatosUsuario /> es un componente que obtiene información de un usuario desde una API. Mientras los datos están siendo recuperados, se muestra el fallback que indica al usuario que la información está cargándose.

El componente <DatosUsuario /> podría implementarse de la siguiente manera:

<template>
  <div>
    <h1>{{ usuario.nombre }}</h1>
    <p>Correo electrónico: {{ usuario.email }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

interface Usuario {
  nombre: string
  email: string
}

const usuario = ref<Usuario | null>(null)

async function obtenerDatosUsuario() {
  const respuesta = await fetch('https://api.ejemplo.com/usuario/1')
  usuario.value = await respuesta.json()
}

await obtenerDatosUsuario()
</script>

En este componente, la función setup es asíncrona y utiliza await para obtener los datos del usuario antes de continuar con el renderizado. De esta forma, Suspense detecta que el componente está en estado de suspensión hasta que la promesa se resuelve.

Es importante destacar que al utilizar await en setup, debemos asegurarnos de que el código de obtención de datos esté dentro de una función asíncrona. Esto garantiza que el componente espere a que los datos estén listos antes de renderizarse.

Si se desean manejar errores durante la obtención de datos, se puede emplear un bloque try...catch:

<script setup lang="ts">
import { ref } from 'vue'

interface Usuario {
  nombre: string
  email: string
}

const usuario = ref<Usuario | null>(null)
const error = ref<Error | null>(null)

async function obtenerDatosUsuario() {
  try {
    const respuesta = await fetch('https://api.ejemplo.com/usuario/1')
    if (!respuesta.ok) {
      throw new Error('Error al obtener los datos del usuario')
    }
    usuario.value = await respuesta.json()
  } catch (e) {
    error.value = e as Error
  }
}

await obtenerDatosUsuario()
</script>

<template>
  <div v-if="error">
    <p>Error: {{ error.message }}</p>
  </div>
  <div v-else>
    <h1>{{ usuario.nombre }}</h1>
    <p>Correo electrónico: {{ usuario.email }}</p>
  </div>
</template>

En este caso, si ocurre un error durante la petición, se almacena en la variable error y podemos mostrar un mensaje al usuario indicando lo sucedido.

Al combinar Suspense con obtención de datos asíncrona, es posible mejorar el manejo de estados de carga y errores en la aplicación. El componente Suspense espera a que todas las promesas dentro de sus componentes hijos se resuelvan antes de renderizar el contenido, mostrando el fallback mientras tanto.

Además, si se necesita utilizar múltiples fuentes de datos o realizar varias peticiones simultáneamente, Suspense gestionará todas las promesas de forma conjunta:

<template>
  <Suspense>
    <template #default>
      <div>
        <DatosUsuario />
        <ListaProductos />
      </div>
    </template>
    <template #fallback>
      <div>Cargando información...</div>
    </template>
  </Suspense>
</template>

En este ejemplo, tanto <DatosUsuario /> como <ListaProductos /> realizan peticiones asíncronas. Suspense mostrará el contenido del fallback hasta que ambas peticiones se resuelvan, proporcionando una interfaz coherente al usuario.

Es esencial recordar que para que Suspense funcione correctamente con la obtención de datos, las operaciones asíncronas deben estar dentro de un setup asíncrono o retornar promesas que Suspense pueda detectar. De esta manera, se aprovecha al máximo esta funcionalidad para mejorar el rendimiento y la usabilidad de la aplicación.

Al utilizar Suspense de esta forma, se facilita el manejo de estados de carga y errores, y se proporciona una experiencia más sólida al usuario. Integrar Suspense con fetching de datos es una práctica recomendada para mantener el código limpio y mejorar la eficiencia de nuestras aplicaciones Vue.

Combinación de Suspense con otros composables

El uso de Suspense no se limita únicamente a componentes asíncronos o a la obtención de datos; también puede integrarse eficazmente con otros composables personalizados en Vue. Los composables son funciones reutilizables que encapsulan lógica reactiva y pueden incluir operaciones asíncronas. Al combinar Suspense con composables, podemos gestionar estados de carga y errores de manera más coherente en nuestra aplicación.

Cuando se utilizan composables que realizan operaciones asíncronas, es importante que el componente que los consume pueda esperar a que dichas operaciones se completen antes de renderizarse. Para lograr esto, se puede hacer que el setup del componente sea una función asíncrona utilizando async, y luego utilizar await en los composables asíncronos. De esta manera, Suspense detectará que el componente está en un estado pendiente y mostrará el contenido de reserva definido en el fallback.

Por ejemplo, consideremos un composable useDatos que obtiene datos de una API:

// useDatos.ts
import { ref } from 'vue'

export async function useDatos() {
  const datos = ref(null)

  async function fetchDatos() {
    const respuesta = await fetch('https://api.ejemplo.com/datos')
    datos.value = await respuesta.json()
  }

  await fetchDatos()

  return {
    datos
  }
}

Este composable encapsula la lógica para obtener datos y devuelve una referencia reactiva datos. Ahora, en nuestro componente, podemos utilizar este composable dentro de un setup asíncrono:

<script setup lang="ts">
import { useDatos } from './useDatos'

const { datos } = await useDatos()
</script>

<template>
  <div>
    <p>Datos: {{ datos }}</p>
  </div>
</template>

Al utilizar await useDatos(), nos aseguramos de que el componente espere a que el composable complete su operación asíncrona antes de continuar. Cuando este componente se envuelve dentro de un Suspense, podemos gestionar el estado de carga de manera efectiva:

<template>
  <Suspense>
    <template #default>
      <ComponenteConDatos />
    </template>
    <template #fallback>
      <div>Cargando datos...</div>
    </template>
  </Suspense>
</template>

Es importante destacar que los composables pueden manejar no solo la carga de datos, sino también gestión de errores y otras operaciones asíncronas. Al centralizar esta lógica en composables, hacemos que nuestro código sea más modular y mantenible.

Otro ejemplo es un composable que escucha eventos WebSocket:

// useWebSocket.ts
import { ref } from 'vue'

export function useWebSocket(url: string) {
  const mensaje = ref(null)
  const conectado = ref(false)

  const socket = new WebSocket(url)

  const conectadoPromise = new Promise<void>((resolve, reject) => {
    socket.onopen = () => {
      conectado.value = true
      resolve()
    }

    socket.onerror = (err) => {
      conectado.value = false
      reject(err)
    }
  })

  socket.onmessage = (event) => {
    mensaje.value = event.data
  }

  return {
    mensaje,
    conectado,
    conectadoPromise
  }
}

En este caso, el composable useWebSocket establece una conexión WebSocket y expone referencias reactivas. Si necesitamos esperar a que la conexión se establezca antes de renderizar el componente, podemos utilizar Suspense junto con una promesa que se resuelva cuando conectado sea true:

<script setup lang="ts">
import { useWebSocket } from './useWebSocket'

const { mensaje, conectado, conectadoPromise } = useWebSocket('wss://ejemplo.com/socket')

await conectadoPromise
</script>

<template>
  <div>
    <p>Conectado: {{ conectado }}</p>
    <p>Mensaje: {{ mensaje }}</p>
  </div>
</template>

Al utilizar await conectadoPromise, el componente esperará a que la conexión se establezca antes de continuar. De nuevo, al envolver este componente en un Suspense, podemos mostrar un indicador de carga mientras la conexión se establece:

<template>
  <Suspense>
    <template #default>
      <ComponenteWebSocket />
    </template>
    <template #fallback>
      <div>Estableciendo conexión...</div>
    </template>
  </Suspense>
</template>

La combinación de Suspense con composables asíncronos nos permite manejar de forma elegante situaciones donde múltiples operaciones asíncronas necesitan completarse antes de que el componente pueda renderizarse correctamente. Esto es especialmente útil en aplicaciones complejas donde la sincronización de datos es crítica.

Cabe mencionar que también es posible manejar varios composables asíncronos en un solo componente. En tal caso, se pueden utilizar Promise.all para esperar a que todas las promesas se resuelvan:

<script setup lang="ts">
import { useDatos } from './useDatos'
import { usePermisos } from './usePermisos'

const [{ datos }, { permisos }] = await Promise.all([useDatos(), usePermisos()])
</script>

<template>
  <div>
    <p>Datos: {{ datos }}</p>
    <p>Permisos: {{ permisos }}</p>
  </div>
</template>

De esta manera, el componente espera a que tanto useDatos como usePermisos completen sus operaciones asíncronas antes de renderizarse. El Suspense que envuelve al componente gestionará el estado de carga de forma automática.

Además, al estructurar nuestras operaciones asíncronas dentro de composables y utilizar async setup en los componentes, aprovechamos al máximo las capacidades de Suspense para mejorar la experiencia del usuario. Esta combinación permite un código más limpio y mantiene una arquitectura coherente en la aplicación.

Aprende Vuejs GRATIS online

Ejercicios de esta lección Introducción a Suspense

Evalúa tus conocimientos de esta lección Introducción a Suspense con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de Vuejs

Accede a todas las lecciones de Vuejs y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Vue Y Su Ecosistema

Vue.js

Introducción Y Entorno

Instalar Y Configurar Vue Con Vite

Vue.js

Introducción Y Entorno

Introducción A La Sintaxis De Plantillas

Vue.js

Componentes

Introducción A Componentes

Vue.js

Componentes

Componentes Con Options Api

Vue.js

Componentes

Componentes Con Composition Api

Vue.js

Componentes

Renderizado Condicional Con V-if

Vue.js

Componentes

Renderizado Iterativo Con V-for

Vue.js

Componentes

Props Y Comunicación Entre Componentes

Vue.js

Componentes

Manejo De Eventos En Vue Con V-on

Vue.js

Componentes

Binding Bidireccional Con V-model Y Definemodel

Vue.js

Componentes

Estilización De Componentes

Vue.js

Componentes

Reactividad Con Ref Y Reactive

Vue.js

Composición Y Reactividad

Ciclo De Vida Con Composition Api

Vue.js

Composición Y Reactividad

Composition Api: Provide E Inject

Vue.js

Composición Y Reactividad

Introducción A Los Composables

Vue.js

Composición Y Reactividad

Uso Avanzado De Composables

Vue.js

Composición Y Reactividad

Introducción A Vue Router

Vue.js

Navegación Y Enrutamiento

Definición Y Manejo De Rutas

Vue.js

Navegación Y Enrutamiento

Rutas Anidadas Y Dinámicas

Vue.js

Navegación Y Enrutamiento

Navegación Programática Y Redirección

Vue.js

Navegación Y Enrutamiento

Solicitudes Http Con Fetch Api

Vue.js

Interacción Http Con Apis De Backend

Solicitudes Http Con Axios

Vue.js

Interacción Http Con Apis De Backend

Introducción A Suspense

Vue.js

Interacción Http Con Apis De Backend

Evaluación Test De Conocimientos Vuejs

Vue.js

Evaluación

Accede GRATIS a Vuejs y certifícate

Certificados de superación de Vuejs

Supera todos los ejercicios de programación del curso de Vuejs y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  1. Comprender el concepto y los usos de Suspense en Vue.js.
  2. Implementar Suspense para gestionar componentes y datos asíncronos.
  3. Configurar Suspense con default y fallback slots.
  4. Manejar errores y estados de carga en componentes asíncronos.
  5. Integrar Suspense con composables y otras funciones reactivas.