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.
Object.defineProperty: Convierte las propiedades de data en “getter” y “setter” para detectar cambios.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.
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:
javascript this.myArray[2] = 'new value'; // Vue no detecta este cambioPara 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
Vue.set() o this.$set() para garantizar la reactividad.Ejemplo con Vue.set() o this.$set():javascript this.$set(this.myArray, 5, 'new value'); // Agrega un elemento en el índice 5
Limitaciones con Objetos:
Vue.set() o this.$set(), Vue no podrá hacer que esa propiedad sea reactiva.Ejemplo sin Vue.set():javascript this.user.age = 30; // Vue no detecta este cambioSolución con
Vue.set() o this.$set():javascript this.$set(this.user, 'age', 30); // Vue detecta el cambio
delete, Vue no puede detectar la eliminación de forma reactiva. Si necesitas que Vue actualice la vista después de eliminar una propiedad, puedes usar Vue.delete() o this.$delete().Ejemplo con Vue.delete():javascript this.$delete(this.user, 'age'); // Elimina 'age' de user y Vue actualiza la vista
Resumen de las Limitaciones y Soluciones:
push(), pop(), splice()) o Vue.set() para hacer cambios reactivos en los índices específicos.Vue.set() o this.$set() para agregar propiedades reactivas, y Vue.delete() o this.$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:
data y convierte sus propiedades en reactivas utilizando Object.defineProperty(). Esto permite que Vue sepa cuándo se accede a o se modifica un valor.data (por ejemplo, {{ message }}), Vue registra esa propiedad como una dependencia de ese componente. Esto se realiza a través del getter de la propiedad.this.user.age = 30), Vue no puede detectar este cambio de manera reactiva a menos que uses Vue.set() o this.$set().arr[2] = 'new value'). Para cambios detectables, debes usar métodos como splice() o push().Object.defineProperty(): Convierte las propiedades de data en “reactivas” (con getter y setter).```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 al data 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
createdjavascript
created() {
this.fetchData();
}mounted
mountedCHARTJS
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
updatedjavascript
updated() {
this.adjustLayout();
}destroyed
destroyedjavascript
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:
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!');
}
}setInterval o setTimeout 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);
}axios.get('/api/data', { cancelToken: cancelToken.token })
.then(response => {
this.apiData = response.data;
})
.catch(error => {
if (axios.isCancel(error)) {
console.log('Request cancelled');
}
}); } } ```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 que destroyed se ejecuta después. En general, usa destroyed para la mayoría de las tareas de limpieza, ya que en beforeDestroy 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:
apiService.js para manejar peticiones HTTP en lugar de hacerlo directamente en el componente.javascript
// validationService.js
export function validateForm(data) {
// Lógica de validación --------sadadasdad---
return { valid: true, errors: [] };
}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:
Button que reciba props para personalizar el estilo y el comportamiento:<script>
export default {
props: {
btnClass: { type: String, default: 'btn-primary' },
onClick: { type: Function, default: () => {} },
},
};
</script>```Mínimo ${length} caracteres;// validatorService.jsEn el componente:javascript
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:
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'],
},
};
},
};inject para acceder a las dependencias proporcionadas por el padre.javascript
export default {
inject: ['myService'],
mounted() {
console.log(this.myService.getData()); // ['item1', 'item2', 'item3']
},
};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.
AdminCard requiere algo extra (por ejemplo, adminLevel) pero no puede manejar la falta de ese dato, rompe el LSP.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í:
typescript
interface UserService {
getUser(): string;
updateUser(name: string): void;
deleteUser(): void;
}Admin updated to ${name});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
},
});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:
provide/inject:provide y inject de Vue para desacoplar componentes de implementaciones específicas.export default defineComponent({
import { defineComponent, inject } from "vue-property-decorator";
import { ILoggerService } from "@/services/LoggerService";
const logMessage = () => {
loggerService.log("Hello, Dependency Inversion Principle!");
};
return { logMessage };
}, }); </script><template>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:
EventBus.$emit('evento', datos).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.
¿Cuándo usar Vuex y cuándo un estado alternativo?