Composición y encadenamiento de composables
En Vue, los composables son funciones reutilizables que encapsulan lógica reactiva. La composición de composables permite combinar múltiples composables y crear abstracciones más complejas. El encadenamiento de composables se refiere a la práctica de utilizar la salida de un composable como entrada de otro, lo que permite una orquestación fluida de la lógica.
Para ilustrar la composición, considere un composable que maneja el estado de autenticación de un usuario:
// useAuth.js
import { ref } from 'vue';
export function useAuth() {
const isAuthenticated = ref(false);
function login() {
isAuthenticated.value = true;
}
function logout() {
isAuthenticated.value = false;
}
return { isAuthenticated, login, logout };
}
Ahora, imagine que desea crear un nuevo composable que también maneje la autorización basada en roles. Puede componer este nuevo composable utilizando useAuth
:
// useRole.js
import { ref } from 'vue';
import { useAuth } from './useAuth';
export function useRole() {
const role = ref("invitado")
function changeRoleToAdmin() {
role.value = "admin";
}
return { useAuth, role, changeRoleToAdmin };
}
En este ejemplo, useAuthorization
utiliza useAuth
para determinar si un usuario está autenticado antes de verificar su rol. Esto demuestra cómo los composables pueden componerse para crear lógica más rica y compleja.
El encadenamiento de composables se puede observar cuando un composable depende de la salida de otro. Por ejemplo, si tiene un composable que maneja la configuración de la aplicación, puede encadenarlo con useAuthorization
para condicionar la configuración según el rol del usuario:
<!-- App.vue -->
<template>
<div ref="div" style="padding: 1em;">
Tema para el usuario {{ userRole }}
</div>
<br>
<button v-if="!isAuthenticated" @click="register()">Iniciar
sesión</button>
<button v-else type="button" @click="changeRoleToAdmin()">Cambiar rol a Admin</button>
<button type="button" @click="signOut()">Cerrar sesión</button>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useRole } from '../composables/useRole';
const div = ref<HTMLElement>();
const { useAuth, role, changeRoleToAdmin } = useRole();
const { isAuthenticated, login, logout } = useAuth()
const canAccessAdmin = computed(() => isAuthenticated.value && role.value === 'admin');
const userRole = computed(() => role.value)
function register() {
login();
role.value = 'registrado';
}
function signOut() {
logout();
role.value = 'invitado';
}
watch(canAccessAdmin, (newValue) => {
console.log("el valor ha pasado de: ", canAccessAdmin, " a: ", newValue);
if (newValue) {
div.value!.style.background = "black"
div.value!.style.color = "white"
} else {
div.value!.style.background = "white"
div.value!.style.color = "black"
}
});
</script>
Aquí, se observa los cambios en canAccessAdmin
de useAuthorization
para ajustar la configuración de la aplicación. Este encadenamiento permite que la lógica de configuración reaccione automáticamente a los cambios en la autorización del usuario.
La práctica de componer y encadenar composables facilita la creación de aplicaciones modulares y mantenibles, permitiendo a los desarrolladores dividir la lógica en piezas manejables y reutilizables.
¿Te está gustando esta lección?
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
Inyección de dependencias en composables
En Vue, la inyección de dependencias en composables es una técnica que permite a los desarrolladores compartir y gestionar dependencias de manera eficiente a través de los diferentes niveles de la jerarquía de componentes. Esto se logra mediante el uso de las funciones provide
y inject
, que facilitan el paso de datos o funciones desde un proveedor hasta un consumidor sin necesidad de prop drilling.
Para implementar la inyección de dependencias en composables, primero se define un proveedor que utiliza provide
para exponer una dependencia. Luego, en cualquier componente o composable descendiente, se utiliza inject
para acceder a esa dependencia.
Ejemplo de uso básico de provide
e inject
en un composable:
// useThemeProvider.js
import { provide, ref } from 'vue';
export function useThemeProvider() {
const theme = ref('light');
provide('theme', theme);
return { theme };
}
En este ejemplo, useThemeProvider
es un composable que define un estado reactivo theme
y lo proporciona a través del árbol de componentes. Cualquier componente descendiente puede inyectar este theme
y reaccionar a sus cambios.
Para consumir esta dependencia en un composable o componente, se utiliza inject
de la siguiente manera:
// useThemeConsumer.js
import { inject } from 'vue';
export function useThemeConsumer() {
const theme = inject('theme', ref('defaultTheme'));
return { theme };
}
Aquí, useThemeConsumer
inyecta el theme
proporcionado. En caso de que no haya un proveedor en la jerarquía, se utilizará el valor predeterminado defaultTheme
.
La inyección de dependencias es especialmente útil en aplicaciones grandes o complejas, donde es necesario compartir configuraciones o servicios entre múltiples componentes sin crear dependencias fuertes. No obstante, se debe usar con cuidado para evitar la creación de dependencias ocultas que puedan dificultar la comprensión del flujo de datos en la aplicación.
En situaciones avanzadas, como la inyección de servicios o instancias de clases, se puede proporcionar e inyectar funciones o clases completas, lo que permite un alto grado de flexibilidad y modularidad. Aquí un ejemplo de cómo inyectar un servicio:
// loggerService.js
export class LoggerService {
log(message: string) {
console.log('Log:', message);
}
}
// useLoggerProvider.js
import { provide } from 'vue';
import { LoggerService } from '../../services/loggerServices';
export function useLoggerProvider() {
provide('logger', new LoggerService());
}
// useLoggerConsumer.js
import { inject } from 'vue';
import { LoggerService } from '../../services/loggerServices';
export function useLoggerConsumer() {
const logger = inject<LoggerService>("logger", new LoggerService());
function logMessage(message: string) {
if (logger) {
logger.log(message);
}
}
return { logMessage };
}
En este caso, useLoggerProvider
proporciona una instancia de LoggerService
, y useLoggerConsumer
inyecta y utiliza el servicio para registrar mensajes. Este enfoque permite mantener el código modular y reutilizable, facilitando la gestión de dependencias en aplicaciones Vue.
Creación de composables con async y await
En Vue, los composables son funciones reutilizables que encapsulan lógica reactiva y permiten estructurar el código de manera más modular. Al integrar async
y await
en composables, se puede gestionar de forma eficiente la lógica asíncrona, como llamadas a API, dentro de una aplicación Vue.
Para crear un composable que maneje operaciones asíncronas, es crucial definir la función del composable como async
. Esto permite utilizar await
dentro de la función para esperar la resolución de promesas antes de continuar con la ejecución. Aquí se muestra cómo definir un composable asíncrono:
// useFetchData.js
import { ref } from 'vue';
export async function useFetchData(url) {
const data = ref(null);
const error = ref(null);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
} catch (err) {
error.value = err.message;
}
return { data, error };
}
En este ejemplo, useFetchData
es un composable que realiza una solicitud HTTP para obtener datos desde un url
proporcionado. Utiliza await
para esperar la respuesta de la función fetch
y maneja errores potenciales mediante un bloque try-catch
. Los valores de data
y error
se definen como referencias reactivas que pueden ser utilizadas en componentes para reaccionar a los cambios de estado.
Para utilizar este composable en un componente Vue, se puede hacer de la siguiente manera:
// DataDisplayComponent.vue
<template>
<div>
<div v-if="error">{{ error }}</div>
<div v-else-if="data">
<pre>{{ data }}</pre>
</div>
<div v-else>Cargando...</div>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
import { useFetchData } from './useFetchData.js';
const { data, error } = await useFetchData('https://api.example.com/data');
onMounted(() => {
// Lógica adicional si es necesario al montar el componente
});
</script>
En este componente DataDisplayComponent.vue
, se utiliza el composable useFetchData
para obtener datos de una API. La llamada a useFetchData
se realiza con await
para asegurar que los datos se cargan antes de que el componente se monte completamente, y se gestiona el estado de carga con un simple condicional en la plantilla.
Es importante tener en cuenta que, aunque async
y await
hacen que el código asíncrono sea más fácil de leer y escribir, se debe gestionar adecuadamente el estado de carga y los errores para proporcionar una experiencia de usuario fluida. Además, debido a que las funciones de setup en componentes de composición no pueden ser asíncronas, cualquier lógica asíncrona debe manejarse dentro de funciones auxiliares o composables como se muestra en los ejemplos anteriores.
El uso de composables con async
y await
en Vue mejora la legibilidad del código y facilita la gestión de tareas asíncronas, manteniendo la reactividad y modularidad del código.
Aprendizajes de esta lección
- Componer y encadenar múltiples composables.
- Utilizar inyección de dependencias con
provide
einject
. - Manejar operaciones asíncronas con
async
yawait
en composables. - Implementar patrones avanzados de reutilización de lógica en Vue.
Completa Vuejs y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs