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:
Si value es igual a B, cargamos el componente B, en caso contrario, cargamos el componente A.
Esto es lo que vamos a hacer con los componentes de habilidades, usar el tag <component> y cargar el componente que necesitemos con is. ¡Vamos a ello!
Componentes dinámicos asíncronos 🤯
Empecemos cambiando el HTML de nuestro componente /Hero/HeroSkills/Index.vue:
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: