VUE Flashcards
patrón Presentational/Container
El patrón Container/Presentational separa componentes en dos tipos:
Contenedor:
- Maneja lógica y estado
- Realiza llamadas API
- Procesa datos
- No tiene estilos propios
- Pasa datos via props
Presentacional:
- Renderiza UI
- Recibe datos via props
- Sin lógica compleja
- Emite eventos
- Reutilizable
Ejemplo:
```vue
// UserContainer.vue
<template>
<UserList
:users="users"
@user-select="handleSelect"
/>
</template>
<script> export default { data() { return { users: [] } }, created() { this.fetchUsers() }, methods: { async fetchUsers() { this.users = await api.getUsers() } } } </script>
// UserList.vue (Presentacional)
<template>
<ul>
<li v-for="user in users" @click="$emit('user-select', user)">
{{ user.name }}
</li>
</ul>
</template>
~~~
Beneficios:
- Mejor mantenibilidad
- Componentes reutilizables
- Testing más sencillo
- Separación de responsabilidades
Reactividad en VUE 2
Funcionamiento de la reactividad en Vue 2
Conceptos básicos:
1. Sistema reactivo:
- Los datos en el objeto data
son observados.
- Cuando un valor cambia, Vue actualiza automáticamente la interfaz de usuario.
-
Mecanismos internos:
-
Object.defineProperty
: Convierte las propiedades dedata
en “getter” y “setter” para detectar cambios. - Dependencias: Rastrea qué componentes usan qué propiedades y actualiza solo los componentes afectados cuando una propiedad cambia.
-
Limitaciones:
- Propiedades dinámicas:
- Las propiedades añadidas después de la inicialización no son reactivas, a menos que uses Vue.set()
o this.$set()
.
- Arrays:
- No detecta cambios en índices modificados directamente (arr[index] = value
).
- Detecta cambios si se usan métodos como push
, pop
, shift
, o unshift
.
Mejora de rendimiento:
1. Usa Vue.set()
o this.$set()
para agregar propiedades reactivas.
2. Usa v-once
para renderizar contenido estático solo una vez.
Resumen:
Este sistema permite que Vue 2 sea eficiente en la actualización de interfaces.
Vue.set()
y this.$set()
en Vue 2:
Vue.set()
y this.$set()
en Vue 2:
En Vue 2, cuando se agregan nuevas propiedades a un objeto después de la inicialización, esas propiedades no son reactivas por defecto. Para hacerlas reactivas, se debe usar Vue.set()
o this.$set()
, lo que permite a Vue observar las nuevas propiedades y actualizar la vista si cambian.
-
Vue.set(obj, key, value)
: Usado para agregar una propiedad reactiva a un objeto. -
this.$set(obj, key, value)
: Es la versión de instancia del mismo método, usado dentro de los métodos de los componentes.
Ejemplo de uso:
```javascript
data: {
user: { name: ‘John’ }
},
methods: {
addAge() {
// Usando Vue.set para hacer que la propiedad ‘age’ sea reactiva
Vue.set(this.user, ‘age’, 30);
}
}
~~~
Al usar Vue.set()
, si modificas user.age
, Vue podrá reaccionar y actualizar la vista correctamente. Sin Vue.set()
, age
no sería reactivo.
Resumen:
- Object.defineProperty
: Usado internamente por Vue para hacer que las propiedades de data
sean reactivas.
- Vue.set()
o this.$set()
: Métodos para agregar propiedades reactivas a objetos después de su creación.
Object.defineProperty en Vue 2
Object.defineProperty
en Vue 2:
Vue 2 usa Object.defineProperty
para hacer que las propiedades del objeto data
sean reactivas. Al convertir las propiedades en “getters” y “setters”, Vue puede interceptar el acceso y las modificaciones a los datos, lo que le permite actualizar la vista automáticamente cuando los datos cambian.
- Getter: Cuando se accede a la propiedad, Vue registra la dependencia (el componente que la usa).
- Setter: Cuando se modifica la propiedad, Vue sabe que debe actualizar la vista.
Ejemplo:
```javascript
const data = { message: ‘Hello’ };
Object.defineProperty(data, ‘message’, {
get() {
// Cuando se accede, Vue registra la dependencia
return this._message;
},
set(value) {
// Cuando se modifica, Vue actualiza la vista
this._message = value;
console.log(‘Message changed:’, value);
}
});
data.message = ‘Hello, Vue!’;
console.log(data.message); // “Hello, Vue!”
~~~
Limitaciones con arrays y objetos
En Vue 2, hay algunas limitaciones con respecto a cómo maneja la reactividad de arrays y objetos. Aunque Vue 2 es muy eficiente en detectar cambios, tiene dificultades con ciertos tipos de modificaciones, especialmente en arrays y objetos. A continuación, te explico estas limitaciones y cómo manejarlas:
Limitaciones con Arrays:
-
Modificación directa de índices:
Vue 2 no puede detectar cambios cuando se modifican los elementos de un array directamente por índice. Por ejemplo:javascript this.myArray[2] = 'new value'; // Vue no detecta este cambio
Para solucionar esto, Vue recomienda usar métodos mutativos del array que son reactivamente observados, como:
-push()
-pop()
-shift()
-unshift()
-splice()
Ejemplo:javascript this.myArray.splice(2, 1, 'new value'); // Vue detecta el cambio
-
Agregar nuevos elementos al array:
Si agregas nuevos elementos fuera de los índices actuales del array, Vue 2 detecta los cambios. Sin embargo, si estás agregando elementos a una posición específica, y no al final o al principio, se recomienda usarVue.set()
othis.$set()
para garantizar la reactividad.Ejemplo conVue.set()
othis.$set()
:javascript this.$set(this.myArray, 5, 'new value'); // Agrega un elemento en el índice 5
Limitaciones con Objetos:
-
Agregar nuevas propiedades:
Vue 2 no puede detectar nuevas propiedades que se agregan a un objeto después de su inicialización. Por ejemplo, si agregas una nueva propiedad a un objeto sin usarVue.set()
othis.$set()
, Vue no podrá hacer que esa propiedad sea reactiva.Ejemplo sinVue.set()
:javascript this.user.age = 30; // Vue no detecta este cambio
Solución conVue.set()
othis.$set()
:javascript this.$set(this.user, 'age', 30); // Vue detecta el cambio
-
Eliminar propiedades:
Cuando eliminas propiedades de un objeto condelete
, Vue no puede detectar la eliminación de forma reactiva. Si necesitas que Vue actualice la vista después de eliminar una propiedad, puedes usarVue.delete()
othis.$delete()
.Ejemplo conVue.delete()
:javascript this.$delete(this.user, 'age'); // Elimina 'age' de user y Vue actualiza la vista
Resumen de las Limitaciones y Soluciones:
-
Arrays: Vue 2 no detecta cambios al modificar directamente los índices de un array. Usa métodos mutativos (
push()
,pop()
,splice()
) oVue.set()
para hacer cambios reactivos en los índices específicos. -
Objetos: Cuando agregas o eliminas propiedades después de la inicialización, Vue no puede detectarlas. Usa
Vue.set()
othis.$set()
para agregar propiedades reactivas, yVue.delete()
othis.$delete()
para eliminarlas de manera reactiva.
Estas limitaciones se solucionan fácilmente utilizando las herramientas que Vue proporciona, y esto garantiza que los cambios sean observados y reflejados en la interfaz de usuario.
Como VUE registra cambios
Vue detecta cambios en los datos mediante un sistema de reactividad basado en getter y setter. El proceso implica un conjunto de pasos que permiten a Vue hacer un seguimiento de los datos y actualizar la interfaz de usuario (DOM) de manera eficiente cada vez que esos datos cambian. A continuación te explico cómo funciona:
-
Transformación de Datos en Reactivos:
Cuando Vue instancia un componente, toma el objetodata
y convierte sus propiedades en reactivas utilizandoObject.defineProperty()
. Esto permite que Vue sepa cuándo se accede a o se modifica un valor.
- Getter: Cuando Vue accede a una propiedad para renderizarla, se registra una “dependencia”. Esto significa que Vue sabe qué vistas (o componentes) dependen de esa propiedad.
- Setter: Cuando se modifica una propiedad reactiva, Vue marca la vista como “sucia”, lo que significa que necesita ser actualizada.
-
Sistema de Dependencias:
- Registro de dependencias: Cuando un componente usa una propiedad dedata
(por ejemplo,{{ message }}
), Vue registra esa propiedad como una dependencia de ese componente. Esto se realiza a través del getter de la propiedad.
- Reactividad: Cuando el valor de esa propiedad cambia, Vue sabe qué componentes o vistas necesitan ser actualizados debido a las dependencias registradas. Esto asegura que solo se actualicen los componentes afectados, mejorando el rendimiento. -
¿Qué ocurre cuando los datos cambian?
Cuando se cambia el valor de una propiedad reactiva:
- El setter se ejecuta, lo que marca la propiedad como “sucia”.
- Vue re-renderiza las partes de la vista que dependen de esa propiedad (esto es eficiente gracias al sistema de dependencias). -
Cómo Vue Actualiza el DOM:
- Cuando una propiedad reactiva cambia, Vue pasa a una fase llamada “digest” (proceso de verificación), en la que Vue compara el estado actual de las propiedades con los estados anteriores. Si algo ha cambiado, Vue actualiza el DOM de manera eficiente.
- Patch: Vue realiza un “parche” en el DOM, actualizando solo los nodos que necesitan ser cambiados, en lugar de volver a renderizar toda la vista. -
Limitaciones de la Detección de Cambios:
Aunque Vue tiene un sistema eficiente para detectar cambios, hay algunas limitaciones:
- Propiedades agregadas dinámicamente: Si se agrega una propiedad a un objeto después de su creación (por ejemplo,this.user.age = 30
), Vue no puede detectar este cambio de manera reactiva a menos que usesVue.set()
othis.$set()
.
- Cambio de índice de arrays: Vue no detecta cambios cuando modificas un índice específico de un array directamente (por ejemplo,arr[2] = 'new value'
). Para cambios detectables, debes usar métodos comosplice()
opush()
. - Resumen del Proceso:
-
Object.defineProperty()
: Convierte las propiedades dedata
en “reactivas” (con getter y setter). - Dependencias: Vue registra qué componentes dependen de qué propiedades.
- Reactividad: Cuando los datos cambian, Vue sabe qué actualizar y lo hace de forma eficiente.
- Ejemplo Básico:
```javascript
new Vue({
data: {
message: ‘Hello Vue!’
},
created() {
console.log(this.message); // Accede a ‘message’, Vue lo registra como una dependencia
}
});
~~~
En este ejemplo, cuando el valor de message
cambia, Vue actualizará automáticamente el DOM en las vistas que dependen de esa propiedad.
Conclusión:
Vue utiliza un sistema de reactividad eficiente basado en getters y setters para detectar cambios y actualizar el DOM solo cuando es necesario. Esto asegura que las aplicaciones de Vue sean rápidas y eficientes.
Ciclo de vida de componentes en Vue:
Ciclo de vida de componentes en Vue:
-
created
: El componente ha sido creado, pero aún no se ha montado en el DOM. Se puede acceder aldata
y a las propiedades computadas. Ideal para inicializar datos o hacer peticiones de API. ACCEDE A TODO PERO NO PODES MODIFICAR EL DOM. -
mounted
: El componente se ha montado en el DOM, lo que significa que el DOM ya está disponible. Es el lugar ideal para interactuar con elementos del DOM, como iniciar animaciones o bibliotecas de terceros. -
updated
: Se ejecuta cada vez que el componente y su DOM son actualizados debido a un cambio en los datos reactivos. Aquí se puede reaccionar ante actualizaciones en el estado del componente. -
beforeDestroy
: Se ejecuta justo antes de que el componente sea destruido. El DOM y el estado reactivo todavía están disponibles, por lo que es ideal para limpiar timers, listeners de eventos o recursos externos antes de que el componente sea eliminado. -
destroyed
: El componente ha sido destruido y su DOM ha sido eliminado. Es el último paso en el ciclo de vida. Aquí se asegura que todo lo relacionado con el componente se haya eliminado.
created
-
created
Uso: Este hook se llama justo después de que el componente ha sido instanciado, pero antes de que se monte en el DOM. Es útil cuando necesitas realizar acciones que no dependen del DOM, pero sí del estado del componente.Casos de uso:
- Inicializar datos: Cargar datos desde una API o base de datos.
- Configurar propiedades: Establecer valores iniciales en los datos reactivos.
- Lógica de negocios previa al montaje: Ejecutar funciones que no requieran acceso al DOM.Ejemplo:javascript created() { this.fetchData(); }
mounted
-
mounted
Uso: Este hook se llama cuando el componente ha sido montado en el DOM, lo que significa que se pueden realizar interacciones con el DOM. Ideal para tareas que requieren acceso al DOM o a bibliotecas externas.
CHARTJS
Casos de uso:
- Acceder al DOM: Realizar manipulaciones directas del DOM, como la inicialización de un slider o una biblioteca de gráficos.
- Eventos de terceros: Integrar con bibliotecas externas que requieren acceso al DOM, como jQuery o D3.js.
- Iniciar animaciones: Ejecutar animaciones que necesitan que el DOM ya esté presente.
Ejemplo:
javascript mounted() { this.$nextTick(() => { this.initializeSlider(); }); }
updated
-
updated
Uso: Este hook se ejecuta cada vez que el componente y su DOM son actualizados, es decir, cuando los datos reactivos del componente cambian. Es útil cuando necesitas realizar acciones basadas en cambios de datos o cuando el DOM necesita ser ajustado después de una actualización.Casos de uso:
- Reacciones a cambios de datos: Actualizar o recalcular algo en el DOM cuando los datos cambian.
- Sincronización con el DOM: Realizar ajustes adicionales en el DOM después de que se haya renderizado, como recalcular tamaños de elementos.
- Lógica basada en el estado del componente: Ejecutar funciones solo cuando los datos del componente hayan cambiado.Ejemplo:javascript updated() { this.adjustLayout(); }
destroyed
-
destroyed
Uso: Este hook se llama cuando el componente ha sido destruido y su DOM ha sido removido. Es el lugar adecuado para limpiar recursos y evitar fugas de memoria, como cancelar solicitudes de red o remover listeners de eventos.Casos de uso:
- Limpiar recursos: Eliminar suscripciones, eventos o temporizadores.
- Destruir objetos de terceros: Liberar recursos de bibliotecas de terceros o herramientas que se hayan integrado en el componente.
- Cancelar peticiones de red: Detener cualquier solicitud de API pendiente.Ejemplo:javascript destroyed() { clearInterval(this.timer); this.removeEventListeners(); }
Resumen:
-
created
: Ideal para inicializar datos, realizar peticiones y configurar el estado antes de que el componente se monte. -
mounted
: Perfecto para trabajar con el DOM, bibliotecas de terceros, o realizar tareas que dependan de que el componente ya esté en la página. -
updated
: Usado para reaccionar a cambios en los datos reactivos, actualizar el DOM o realizar ajustes tras una actualización. -
destroyed
: Usado para limpiar recursos, cancelar suscripciones y evitar fugas de memoria cuando el componente se destruye.
Cada uno de estos hooks ofrece un momento específico en el ciclo de vida del componente para realizar tareas de manera eficiente y efectiva.
Manejo de limpieza de recursos
El manejo de limpieza de recursos es crucial en aplicaciones Vue para evitar fugas de memoria y garantizar un rendimiento eficiente. Esto es especialmente importante cuando trabajas con recursos externos como suscripciones a eventos, temporizadores, peticiones de red o bibliotecas de terceros que necesitan ser detenidas o limpiadas cuando un componente se destruye.
¿Por qué es importante limpiar recursos?
Cuando los componentes se destruyen y no se limpian adecuadamente sus recursos (como listeners de eventos, intervalos de tiempo, conexiones de red, etc.), estos recursos permanecen en memoria, lo que puede causar fugas de memoria y afectar el rendimiento de la aplicación.
Casos Comunes para Limpiar Recursos:
-
Event Listeners (Escuchadores de eventos):
Si un componente escucha eventos globales o eventos en el DOM, debes asegurarte de eliminar estos listeners cuando el componente se destruya.Ejemplo:javascript mounted() { // Agregar un listener de evento global window.addEventListener('resize', this.handleResize); }, destroyed() { // Limpiar el listener cuando el componente se destruya window.removeEventListener('resize', this.handleResize); }, methods: { handleResize() { console.log('Window resized!'); } }
-
Timers (Temporizadores):
Si usassetInterval
osetTimeout
en tu componente, debes limpiarlos cuando el componente se destruya para evitar que continúen ejecutándose innecesariamente.Ejemplo:javascript mounted() { // Iniciar un temporizador this.timer = setInterval(() => { console.log('Tick!'); }, 1000); }, destroyed() { // Limpiar el temporizador clearInterval(this.timer); }
-
Conexiones de red o peticiones API:
Si realizas peticiones de red en tus componentes, es importante asegurarse de que no haya solicitudes pendientes cuando el componente se destruye, especialmente si estas peticiones están actualizando el estado del componente.Ejemplo:
```javascript
data() {
return {
apiData: null
};
},
mounted() {
this.fetchData();
},
destroyed() {
// Cancelar cualquier solicitud de red si es necesario
if (this.cancelRequest) {
this.cancelRequest();
}
},
methods: {
fetchData() {
// Imagina que esta función retorna un objeto que tiene un método ‘cancel’
const cancelToken = axios.CancelToken.source();
this.cancelRequest = cancelToken.cancel;axios.get('/api/data', { cancelToken: cancelToken.token }) .then(response => { this.apiData = response.data; }) .catch(error => { if (axios.isCancel(error)) { console.log('Request cancelled'); } }); } } ```
-
Bibliotecas de terceros:
Algunas bibliotecas de terceros pueden requerir limpieza o destrucción manual de recursos, como la eliminación de elementos del DOM, la detención de animaciones o la destrucción de instancias.Ejemplo:javascript mounted() { // Inicializar una biblioteca de terceros this.slider = new SomeSliderLibrary('#slider'); this.slider.init(); }, destroyed() { // Limpiar la biblioteca externa if (this.slider) { this.slider.destroy(); } }
Métodos de Vue para Manejo de Recursos:
-
destroyed
/beforeDestroy
: Estos hooks son ideales para la limpieza de recursos.beforeDestroy
se ejecuta antes de que Vue destruya el componente, mientras quedestroyed
se ejecuta después. En general, usadestroyed
para la mayoría de las tareas de limpieza, ya que enbeforeDestroy
aún no se ha removido el DOM asociado al componente. -
beforeDestroy
: Si necesitas limpiar recursos antes de que el componente se destruya (por ejemplo, detener animaciones antes de que desaparezca el DOM), usa este hook.
Ejemplo Completo de Limpieza de Recursos:
```javascript
<template>
<div>
<button @click="toggleTimer">Start/Stop Timer</button>
</div>
</template>
<script> export default { data() { return { timer: null, intervalId: null }; }, mounted() { console.log('Component mounted!'); // Inicializar cualquier recurso (ej. temporizador) }, methods: { toggleTimer() { if (this.intervalId) { // Detener el temporizador clearInterval(this.intervalId); this.intervalId = null; } else { // Iniciar un nuevo temporizador this.intervalId = setInterval(() => { console.log('Timer is running'); }, 1000); } } }, destroyed() { // Limpiar cualquier recurso cuando el componente se destruya if (this.intervalId) { clearInterval(this.intervalId); } console.log('Component destroyed!'); } }; </script>
~~~
Resumen:
1. Escuchadores de eventos: Usa addEventListener
y removeEventListener
.
2. Temporizadores: Usa setInterval
y clearInterval
.
3. Peticiones de red: Asegúrate de cancelar peticiones pendientes usando cancelToken
de Axios o manejadores similares.
4. Bibliotecas de terceros: Llama a los métodos de destrucción de las bibliotecas externas cuando ya no las necesites.
La limpieza de recursos es esencial para evitar fugas de memoria y mejorar el rendimiento de la aplicación, especialmente en aplicaciones grandes que manejan múltiples componentes y recursos.
¿Cómo se aplica el principio de responsabilidad única (SRP) de SOLID en un proyecto de Vue 2?
Para aplicar el SRP en Vue 2:
-
Dividir componentes:
- Cada componente debe tener una única responsabilidad, por ejemplo:
- Un componente para la interfaz de usuario (UI).
- Otro para manejar lógica específica, como formularios o listas.
- Evita que un solo componente maneje múltiples responsabilidades.
- Cada componente debe tener una única responsabilidad, por ejemplo:
-
Organización del código:
- Mueve funciones reutilizables o específicas a módulos externos (como servicios o helpers).
- Ejemplo: Crea un archivo
apiService.js
para manejar peticiones HTTP en lugar de hacerlo directamente en el componente.
-
Separar preocupaciones:
- Usa Mixins o Composition API (en Vue 3) para extraer lógica compartida.
- Mantén la lógica del negocio fuera del componente. Implementa un patrón como el Controlador (Controller) para separar la vista de la lógica.
-
Ejemplo práctico:
- En lugar de manejar directamente la lógica de un formulario en un componente, crea un archivo externo para validaciones:
javascript // validationService.js export function validateForm(data) { // Lógica de validación --------sadadasdad--- return { valid: true, errors: [] }; }
En el componente:
```javascript
import { validateForm } from ‘./validationService’;export default {
methods: {
handleSubmit() {
const result = validateForm(this.formData);
if (!result.valid) {
this.errors = result.errors;
}
},
},
};
```
- En lugar de manejar directamente la lógica de un formulario en un componente, crea un archivo externo para validaciones:
Este enfoque asegura que cada pieza del código tiene una responsabilidad clara, es más fácil de mantener y se adhiere al principio SRP de SOLID.
Open/Closed Principle vue2
Pregunta:
¿Cómo se aplica el principio de abierto/cerrado (Open/Closed Principle) de SOLID en un proyecto de Vue 2?
Respuesta:
Para aplicar el principio de abierto/cerrado (OCP) en Vue 2:
-
Definición del OCP:
- El código debe ser abierto para extensión pero cerrado para modificaciones. Esto significa que puedes agregar nueva funcionalidad sin cambiar el código existente.
-
Usar componentes reutilizables:
- Crea componentes genéricos que puedan ser extendidos.
- Ejemplo: Un componente de
Button
que reciba props para personalizar el estilo y el comportamiento:
```javascript
<template>
<button :class="btnClass" @click="onClick">
<slot></slot>
</button>
</template><script> export default { props: { btnClass: { type: String, default: 'btn-primary' }, onClick: { type: Function, default: () => {} }, }, }; </script>
```
Ahora, puedes extenderlo para crear botones específicos sin modificar el componente base.
-
Usar estrategias o inyección de dependencias:
- Implementa patrones como el Strategy para cambiar comportamientos sin modificar el código base.
- Ejemplo: Un sistema de validación extensible:
```javascript
// validators.js
export const required = value => value ? null : ‘Campo requerido’;
export const minLength = length => value =>
value.length >= length ? null :Mínimo ${length} caracteres
;// validatorService.js
export function validate(value, rules) {
return rules.map(rule => rule(value)).filter(error => error);
}En el componente:
javascript
import { validate } from ‘./validatorService’;
import { required, minLength } from ‘./validators’;export default {
data() {
return { formData: ‘’, errors: [] };
},
methods: {
validateForm() {
this.errors = validate(this.formData, [required, minLength(3)]);
},
},
};
```
-
Plugins o mixins:
- Usa mixins o plugins para extender la funcionalidad de Vue de forma modular y sin modificar los componentes existentes.
Este enfoque respeta el OCP al permitir agregar funcionalidades sin alterar el código ya implementado, lo que mejora la mantenibilidad y escalabilidad.
¿Cómo se implementa la inyección de dependencias en Vue 2?
En Vue 2, puedes implementar inyección de dependencias utilizando las propiedades provide
y inject
. Estas permiten compartir dependencias entre un componente padre y sus descendientes sin necesidad de pasarlas explícitamente a través de props.
Pasos para implementar la inyección de dependencias:
-
Define las dependencias en el componente padre:
- Usa la propiedad
provide
para especificar qué dependencias estarán disponibles para los descendientes.
javascript export default { provide() { return { myService: this.myService, }; }, data() { return { myService: { getData: () => ['item1', 'item2', 'item3'], }, }; }, };
- Usa la propiedad
-
Consume las dependencias en componentes hijos:
- Usa la propiedad
inject
para acceder a las dependencias proporcionadas por el padre.
javascript export default { inject: ['myService'], mounted() { console.log(this.myService.getData()); // ['item1', 'item2', 'item3'] }, };
- Usa la propiedad
Ventajas:
- Desacoplamiento: Los componentes hijos no necesitan conocer la implementación exacta del servicio o dependencia.
- Mantenibilidad: Cambiar la lógica de las dependencias en el padre no requiere modificaciones en los hijos.
¿Cómo se aplica el principio de sustitución de Liskov (LSP) en un proyecto de Vue 2?
El principio de sustitución de Liskov (LSP), parte de los principios SOLID, establece que “si ( S ) es un subtipo de ( T ), entonces los objetos de tipo ( T ) pueden ser reemplazados por objetos de tipo ( S ) sin alterar el comportamiento correcto del programa”. Esto significa que las subclases deben ser completamente intercambiables con sus clases base sin introducir errores.
En un proyecto con Vue 2, el LSP puede aplicarse al diseñar componentes o servicios para garantizar que las extensiones o reemplazos de estos sean coherentes con las expectativas del programa principal.
Ejemplo práctico
Supongamos que tienes un componente base para mostrar tarjetas de usuario:
Componente base: UserCard
```vue
<template>
<div>
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
</template>
<script> export default { props: { user: { type: Object, required: true, }, }, }; </script>
Subcomponente: `AdminCard` Ahora, creamos una subclase (componente) que extiende `UserCard`, agregando información adicional para administradores. ```vue <template> <div class="user-card admin-card"> <h3>{{ user.name }} (Admin)</h3> <p>{{ user.email }}</p> <p>{{ user.adminLevel }}</p> </div> </template> <script> import UserCard from './UserCard.vue'; export default { extends: UserCard, }; </script>
Aplicación del LSP
Ambos componentes (UserCard
y AdminCard
) deben ser intercambiables en cualquier contexto donde se espere una UserCard
. Por ejemplo:
Usando un array de usuarios mixtos
```vue
<template>
<div>
<component
v-for="(user, index) in users"
:is="user.isAdmin ? 'AdminCard' : 'UserCard'"
:key="index"
:user="user"
/>
</div>
</template>
<script> import UserCard from './UserCard.vue'; import AdminCard from './AdminCard.vue'; export default { components: { UserCard, AdminCard, }, data() { return { users: [ { name: 'Juan', email: 'juan@example.com', isAdmin: false }, { name: 'Ana', email: 'ana@example.com', adminLevel: 'Super', isAdmin: true }, ], }; }, }; </script>
~~~
Cómo evitar violaciones al LSP
1. Mantén contratos claros: Define qué propiedades y comportamientos se esperan de las clases base. Por ejemplo, todos los objetos deben tener al menos name
y email
.
-
Evita agregar requisitos adicionales: Si
AdminCard
requiere algo extra (por ejemplo,adminLevel
) pero no puede manejar la falta de ese dato, rompe el LSP. -
Prueba sustituciones: Asegúrate de que cualquier subcomponente (como
AdminCard
) pueda ser usado sin modificaciones en los contextos donde se usa el componente base (UserCard
).
De esta forma, aplicas el principio de Liskov en el desarrollo de componentes en Vue 2, garantizando que tu código sea flexible y extensible sin perder robustez.
interface surrogation
Respuesta:
El Principio de Segregación de Interfaces (ISP) establece que los clientes no deben depender de interfaces que no utilizan. En otras palabras, es mejor tener varias interfaces específicas que una interfaz genérica grande. En Vue 2 con TypeScript, puedes aplicarlo así:
-
Evita interfaces genéricas innecesarias:
Si defines una interfaz que agrupa muchas responsabilidades, los clientes que no usen todas esas funcionalidades estarán sobrecargados.
Ejemplo de mala práctica:typescript interface UserService { getUser(): string; updateUser(name: string): void; deleteUser(): void; }
-
Divide interfaces grandes en interfaces específicas:
Crea interfaces más pequeñas basadas en las responsabilidades reales de los clientes.
Ejemplo de buena práctica:
```typescript
interface UserReader {
getUser(): string;
}interface UserWriter {
updateUser(name: string): void;
}interface UserDeleter {
deleteUser(): void;
}
``` -
Implementa clases que cumplan interfaces específicas:
Cada clase debe implementar solo las interfaces necesarias para su funcionalidad.
```typescript
class AdminService implements UserReader, UserWriter, UserDeleter {
getUser(): string {
return “Admin User”;
}
updateUser(name: string): void {
console.log(Admin updated to ${name}
);
}
deleteUser(): void {
console.log(“Admin deleted”);
}
}class ViewerService implements UserReader {
getUser(): string {
return “Viewer User”;
}
}
``` -
Usa las interfaces específicas en tus componentes Vue:
Los componentes deben depender solo de las interfaces que realmente necesitan.typescript export default Vue.extend({ data() { return { userService: {} as UserReader, // Solo lectura }; }, methods: { showUser() { console.log(this.userService.getUser()); }, }, created() { this.userService = new ViewerService(); // No depende de métodos innecesarios }, });
-
Cumple ISP:
- Define interfaces específicas para cada caso de uso.
- Asegúrate de que las clases no implementen métodos innecesarios.
Nota:
Romper ISP ocurre cuando un cliente necesita implementar métodos que no utiliza, creando dependencias innecesarias y dificultando la escalabilidad. Evítalo separando responsabilidades de manera lógica.
Inversion de Dependencias
Para aplicar el principio de inversión de dependencias (DIP) en un proyecto con Vue 2 y TypeScript:
-
Definir interfaces para abstraer dependencias:
Declara una interfaz que defina los métodos y propiedades necesarias. Esto permite que el código dependa de una abstracción, no de una implementación concreta.
```typescript
// services/LoggerService.ts
export interface ILoggerService {
log(message: string): void;
}export class ConsoleLoggerService implements ILoggerService {
log(message: string): void {
console.log(message);
}
}
``` -
Inyectar dependencias a través de
provide/inject
:
Usa el mecanismo deprovide
yinject
de Vue para desacoplar componentes de implementaciones específicas.
```typescript
// main.ts
import Vue from “vue”;
import { ConsoleLoggerService, ILoggerService } from “./services/LoggerService”;const loggerService: ILoggerService = new ConsoleLoggerService();new Vue({
provide: {
loggerService,
},
render: (h) => h(App),
}).$mount(“#app”);
``` -
Usar la dependencia en los componentes:
Inyecta la abstracción en los componentes que la necesiten, evitando acoplarte a una implementación concreta.
```typescript
// components/MyComponent.vue
export default defineComponent({
import { defineComponent, inject } from "vue-property-decorator";
import { ILoggerService } from "@/services/LoggerService";
name: “MyComponent”,
setup() {
const loggerService = inject<ILoggerService>("loggerService");
if (!loggerService) {
throw new Error("LoggerService not provided!");
}</ILoggerService>const logMessage = () => { loggerService.log("Hello, Dependency Inversion Principle!"); }; return { logMessage }; }, }); </script>
<template>
<button @click="logMessage">Log Message</button>
</template>``` -
Cambiar implementaciones fácilmente:
Si necesitas cambiar la implementación del servicio (por ejemplo, un logger que envíe datos a un servidor), solo necesitas reemplazar la clase proporcionada sin cambiar el código del componente.typescript // services/RemoteLoggerService.ts export class RemoteLoggerService implements ILoggerService { log(message: string): void { // Envía el mensaje a un servidor fetch("/api/logs", { method: "POST", body: JSON.stringify({ message }) }); } }
Conclusión:
El DIP se implementa al depender de interfaces o abstracciones (en este caso, la interfaz ILoggerService
), y al usar provide/inject
para desacoplar componentes de las implementaciones específicas. Esto facilita el mantenimiento y escalabilidad de tu proyecto.
Event Bus
El Event Bus en Vue 2 es una forma de comunicación entre componentes que no están relacionados directamente (como padre-hijo). Se crea utilizando una instancia de Vue para emitir y escuchar eventos globalmente. Es útil para manejar datos o interacciones entre componentes de manera simple.
Por ejemplo:
-
Emisor: Un componente puede enviar un evento con
EventBus.$emit('evento', datos)
. -
Receptor: Otro componente escucha ese evento con
EventBus.$on('evento', callback)
y actúa en consecuencia.
¿Cuáles son las ventajas de usar Vuex?
Centralización: Todo el estado compartido está en un único lugar (store).
Reactividad: Los cambios en el store actualizan automáticamente los componentes que dependen de esos datos.
Herramientas integradas: Soporte para depuración con Vue Devtools.
Escalabilidad: Ideal para proyectos grandes con datos complejos.
¿Qué es Vuex?
Vuex es la librería oficial de gestión de estado para aplicaciones Vue. Proporciona un patrón centralizado para manejar datos compartidos y facilita la comunicación entre componentes en proyectos Vue 2.
¿Cuáles son las desventajas de Vuex?
Complejidad: Requiere configuración adicional y más código (mutations, actions).
Verborrea: Necesita boilerplate para tareas simples.
**Curva de aprendizaje: **Puede ser abrumador para principiantes o proyectos pequeños.
¿Cuáles son las alternativas a Vuex en Vue 2?
Back
1. Event Bus:
Comunicación directa entre componentes usando $emit
y $on
.
- Simplicidad para aplicaciones pequeñas.
- Difícil de mantener en proyectos grandes.
-
Props y $emit:
Ideal para comunicar datos entre componentes padre e hijo.- Fácil y directo para jerarquías simples.
- Ineficiente para compartir datos globales.
-
Plugins personalizados:
Crear un objeto global con Vue.observable o Vue.prototype.- Flexibilidad para datos globales pequeños.
- Sin herramientas de depuración como Vuex.
-
Pinia (experimental en Vue 2):
Librería más ligera y moderna, diseñada para Vue 3, pero compatible con Vue 2.- Menos boilerplate que Vuex.
- Aún inmadura para Vue 2.
¿Cuándo usar Vuex y cuándo un estado alternativo?
-
Usa Vuex si:
Tu proyecto tiene múltiples componentes que comparten datos complejos o necesitas un patrón estructurado. -
Usa un estado alternativo si:
Tu aplicación es pequeña o mediana, y no necesitas la complejidad añadida de Vuex.