Carga Diferida de Componentes en Vue.js: Mejora de Rendimiento
A medida que nuestra aplicación va creciendo, vamos buscando formas de mejorar su rendimiento. Tiene mucho sentido hacer Lazy Loading (o carga diferida) en las rutas, ya que solo abres una ruta por vez. Por lo tanto, no hace falta cargar todo el contenido de las rutas a la vez.
Por si acaso no tenías muy claro el concepto de Lazy Loading, podríamos decir es un patrón de diseño que consiste en retrasar la carga o inicialización de un objeto hasta el momento de su utilización.
Esta técnica ya la hemos utilizado con las rutas de nuestra app, en el router.js. Ahora vamos a ver cómo hacerlo con los componentes.
Actualmente, nuestros componentes de skills activas y pasivas se cargan de forma normal (síncrona). Lo que vamos a hacer ahora es cargar ambos componentes (habilidades activas y habilidades pasivas) con lazy loading, pero solo mostraremos uno, y podremos cambiar entre las activas y las pasivas con unos botones.
Por defecto, estaremos mostrando las habilidades activas, por lo que dicho componente se cargará inmediatamente cuando cargue la vista de /Hero. Sin embargo, el componente de habilidades pasivas no será cargado hasta que pulsemos el botón de Passive, que aún no hemos creado.
Hacer esto en Vue es muy fácil, gracias a los bundlers (como Webpack) que se encargan de separar el código y hacer que se utilice solo cuando es requerido.
Con un ejemplo en el código lo entenderás mejor.
El primer cambio que vamos a hacer es cargar los componentes de manera asíncrona. En vez de hacer el import que hacemos siempre vamos a hacer lo siguiente:
// /Hero/HeroSkills/Index.vue// import ActiveSkills from './ActiveSkills'// import PassiveSkills from './PassiveSkills'exportdefault{name:'HeroSkills',components:{ActiveSkills:()=>import('./ActiveSkills'),PassiveSkills:()=>import('./PassiveSkills')},// ...}
EL import habitual lo comentamos o lo borramos, porque ya no lo vamos a usar. Con esto ya estamos cargando nuestros componentes con lazy loading. Míralo aquí:
Los archivos 18.js y 19.js son nuestros 2 componentes de habilidades. Esto lo sé porque los he abierto y he revisado su contenido, pero esta no es la forma adecuada. ¿Recuerdas como lo hicimos en las rutas 🤔?
En el import pusimos un comentario y esto al generar el chunk de código iba a tener el nombre que nosotros le asignamos.
En código se vería así:
Hasta ahora lo único que hemos hecho es code splitting, es decir, trocear el código (en realidad lo hizo Webpack por nosotros: https://webpack.js.org/guides/code-splitting/). Y esto es así porque estamos mostrando los 2 componentes a la vez, por lo tanto se cargan los 2 a la vez y no hay lazy load.
Lo que puede ser mas interesante es tener 2 botones, cada uno asociado a un componente, y por defecto solo cargar las habilidades activas. Al pulsar el botón de las habilidades pasivas, cargar el contenido de este otro componente, es decir, hacer lazy loading (ahora sí). Vamos a ello:
Vamos a crear una nueva variable en el data para controlar que componente está activo y por ende, cuál mostrar:
data(){return{activeComponent:'ActiveSkills'}}
Y unas computed properties:
computed:{/**
* Dinamyc props for async dynamic components
* @returns{String} */// Con esto estamos generando "props" dinámicas// Si el componente es ActiveSkills pasa como props las activas, si no, las pasivascomponentProps(){returnthis.activeComponent==='ActiveSkills'?this.skills.active:this.skills.passive},// Nos dice si el componente "HabilidadesPasivas" está activo o noisPassiveSkillsActive(){returnthis.activeComponent==='PassiveSkills'}}
En esta parte hacemos un breve hincapié.
Cerramos los ojos y pensamos "¡Qué maravillosas que son las computed properties!" Esta es la potencia de las propiedades computadas.
La regla que yo sigo para saber cuando las tienes que usar es: "Siempre". Abusa de las computadas y todo estará bien.
Hemos generado, a través de componentProps una prop (que pasamos de comp. padre a hijo) dinámica. Esto es necesario porque tenemos componentes dinámicos, por lo tanto las props no pueden ser estáticas como haciamos antes.
Ya por último, tenemos el método que hace que se cargue un componente u otro, cuando hacemos click:
Se ve en 2 columnas porque la app es responsive y así es como se ve en pantallas pequeñas. Ahora bien, si nos fijamos en la pestaña network de las herramientas para desarrolladores, vemos que solo ha cargado el componente de ActiveSkills. ¡Esto es genial!
Sin cerrar la pestaña de Network del navegador, hacemos click en el botón de Passive, veremos como se carga el otro componente.
Hemos hecho lazy load de componentes con tan solo refactorizando un par de líneas. Ahora ya puedes aplicar este método a todos tus proyectos 👏.
Keep Alive
Sin embargo, tengo una (no tan buena) noticia que darte. Al hacer este cambio de pestañas, es decir, el cambio del componente ActiveSkills a PassiveSkills (o viceversa) múltiples veces, estamos haciendo otra vez peticiones de carga de imágenes. Mira lo que ha pasado al hacer el flujo Activas > Pasivas > Activas:
Hemos cargado cada imagen varias veces, en este caso 3 veces. Cada vez que cambiamos de "pestaña", el componente se destruye, por lo tanto las imágenes de las habilidades se tiene que volver a cargar para ser renderizadas.
Esto no es una buena práctica, pues estaríamos haciendo peticiones innecesarias para cargar contenido que antes teníamos cargado. ¡Esto no pasaba cuando teníamos componentes síncronos!
Para ver cómo se crea y se destruye el componente con esta técnica de lazy loading, podemos poner el código que tienes a continuación en el componente de /HeroSkills/PassiveSkills.vue:
¿Qué te ha parecido? ¿Conocías keep-alive? ¿Lo has usado alguna vez? Para mí esta es de las mejores clases del curso.
Realmente algo nuevo para mí 🤯
La verdad que cuando lo conoces por primera vez es mind-blowing 😁Esa fue mi reacción tambien al conocerlo por primera vez :D
Interesante lo del keep-alive, pero entonces me viene a la mente una duda:
Estamos encerrando TODO el componente en un keep alive, ¿Eso significa que no se volverá a cargar nunca más? (Hasta que recarguemos la página obviamente)
Si ese es el caso, entonces no podría refrescar los datos cada vez que cambie de componente ya que sus hooks no se ejecutarían porque ya estarían cargados, y algo que me gusta de Vue es que al volver a cargar el componente puedo refrescar sus datos
es una de las dudas que me surgió también, si llega el profe a ver esta pregunta, podría aclararnos la duda :v
El estado lo puedes cambiar aunque uses keep alive, lo que pasa que al cambiar de tab no se pierde. Lo mas seguro es que no se ejecuten los hooks cuando cambies de tabs. Pruebalo y nos cuentas ;P
Justo cuando pensaba que este curso y Vue no me podrían sorprender más, aparece esta súper clase! Siento que el profe me ha transmitido mucho conocimiento y pasión por Vue. Si lees, gracias Jorge Baumann!
Con Vue3 tuve varios errores y no entendía el porque, luego revisé la documentación y para componentes asíncronos se cargan así:
¡Genial! Los demás ya no tendrán que buscar cómo hacerlo gracias a ti :D 👏
Lo que más amo de Vue es su simpleza para lograr cosas complejas, aprendí muchas cosas super útiles en esta clase.
Se va poniendo interesante cuando llegas al final del curso 😏
Que interesante va todo el curso🤩 estoy ansiosa por terminar
Excelente clase del curso, voy a investigar más sobre keep alive porque tengo varias dudas sobre si vuelve a hacer peticiones o no al server cuando necesite refrescar los datos.
¿Ya lo averiguaste?
Cuando realize el ejercicio note que no se piden de nuevo las imagenes por que quedan en cache, lo probe con Mozilla y Chrome aunque me parecio muy interesate keep alive.
Es una buena feature de Vue :DD
Esta clase estuvo super emocionante !
¡Se pone interesante!
No tenia idea del poder que Lazy Loading con un Keep-alive nos podría brindar en el rendimiento de nuestra aplicación con Vue, genial 🤯
Creo que la documentación de vue falló un poco en este ejemplo. Faltarían ejemplos con casos de uso reales.
Genial 🤯
¿Te voló la cabeza? Esta es una de mis clases favoritas
Ya me habia tocado trabajar con Keep-Alive, tenia un componente con un render de un Mapbox que se podia ocultar, y recargaba todo el componente cuando lo mostraba de nuevo, muy pesado estar solicitando mapas cada momento!
Gran forma de produndizarlo...👍👍
Ese es un buen ejemplo de uso
Pregunta acerca del juego, Los Skills pasivos no tiene Runas? trato de a los Skills pasivos pintarles las rrunas con su nombre y me sale runas undefined
No lo recuerdo ahora. Lo reviso y te comento. Creo que las pasivas no tienen runa, solo las activas.
Confirmado, no tienen runa. Revisa la app original y verás que no aparecen 😅
Siempre que me acerco a Vue.js no deja de sorprenderme con alguna novedad, definitivamente esta fue la gran sorpresa, no tenía ni idea de esta potente característica la cual asumo que con React por ejemplo es posible pero no de una manera tan sencilla.
Saludos compañeros y profesor.
Potente a la vez que sencillo.
Aunque no he probado React, creo que fue una buena elección elegir Vue, al usarlo con pug, todo queda sencillo, legible, y de rápido desarrollo. Se siente lo mismo que trabajar con Python en el Backend. Un problema? estas sencillas lineas de código y pum, todo listo...💪🏽
Esa es la sensación que tuve yo, curva de aprendizaje suave. Con react se me hacia todo mas complicado al principio, no he llegado a profundizar.
como seria si a medida que voy bajando con el scroll y si esta en mi viewport se cargue el componente
Para estos casos tienes que jugar con la altura (height) del documento HTML completo y compararlo con la altura del viewport. Con un evento que compare estos valores para que sucedan cosas en tus componentes.
Eso lo puedes hacer usando el Intersection Observer API
Sonará extraño, pero llevo haciendo este curso con React jajaja. En mi investigación para hacer todo lo de esta clase en React he aprendido muchísimo y al parecer no existe algo como keep-alive por lo que opte por simplemente hacer uso de css para ocultar los componentes y de esta manera no cargar las imágenes cada vez que las mostremos, lo malo de hacerlo de esta manera es que ambos componentes se cargan en simultaneo