Carga perezosa de componentes
Cómo el front-end de Eurmes descarga solo los componentes que cada vista necesita en ese momento, y por qué esa decisión escala mejor a largo plazo.
Cargar componentes Angular bajo demanda
7 capítulos
Dos estrategias para renderizar una lista abierta de componentes dentro de una misma vista sin descargarlos todos: @defer dentro de un switch, y ngComponentOutlet con imports dinámicos. Comparamos qué cuesta mantener cada una. Vídeo en castellano, con subtítulos en castellano, inglés, francés, alemán e italiano.
Ver transcripción del vídeoTranscripción
Muy buenas y bienvenidos a este vídeo, donde voy a compartir con vosotros cómo cargar unos componentes Angular u otros en la misma vista de manera perezosa; es decir, cargar solo aquellos componentes que realmente necesitamos, buscando optimizar lo máximo posible la aplicación. Ojo, no confundir con cargar componentes de manera perezosa según la ruta de Angular en la que nos encontramos: eso es bastante sencillo. Lo que buscamos es que, dentro de una misma ruta, si tenemos una infinidad posible de componentes que renderizar, solo carguemos aquellos que realmente necesitamos en ese momento. Vamos a ver un ejemplo. Imaginemos esta vista, donde vemos una lección teórica con diferentes tipos de componentes: por ejemplo, encabezados con su título; párrafos, que a simple vista parecen simples pero realmente tienen subrayado, negritas, cursiva, enlaces a lugares externos o un audio que te lee ese párrafo. También tenemos otros tipos de componentes, como ejemplos, imágenes generadas con IA, tablas, algunos consejos que podemos darte en la lección teórica, así como un componente de tipo «tu turno», que lo que hace es intentar que el estudiante tome la iniciativa para interiorizar los conceptos de la lección. Nosotros partimos de la base de que, aunque en este caso sea bastante pequeña la cantidad de componentes que hay que cargar y cada uno pese unos pocos kilobytes, queremos hacer una aplicación extensible y mantenible a largo plazo. Es decir, que si luego descubrimos componentes específicos del alemán o del francés para una lección teórica, añadir nuevos componentes no implique que inicialmente tenga que descargarme todos, incluso los específicos de cada idioma. Vamos a ver un ejemplo. Si cargamos esta lección, lo único que tenemos es un párrafo. Si solo tenemos un párrafo, ¿por qué vamos a descargarnos una tabla, un consejo, etcétera? Realmente no hace falta. Si vamos a la pestaña Network y solo nos descargamos esta vista con la caché deshabilitada, es decir, sin memoria del pasado, lo que podemos apreciar es que solo vamos a descargarnos el párrafo. Todo lo que son headers, tablas, imágenes, vídeos, etcétera, no nos lo descargamos porque no lo necesitamos. Si limpiamos y luego navegamos a la siguiente pestaña, «valores personales», lo que vemos es que solo se ha descargado el bloque de vista del header; los otros chunks son cosas menores. Vemos que no se ha descargado el párrafo porque ya lo habíamos descargado previamente y, como en este caso solo hemos añadido el header, solo se descarga el header. Si continuamos en la siguiente lección, lo que podemos ver es que en este caso se descargan ejemplos y tablas: y efectivamente tenemos ejemplos y tablas. Si además navegamos a la siguiente ruta, nos descargamos texto con imagen, que es lo que vemos aquí, además de consejos y también «tu turno». A simple vista puedes decir: «Michael, ¿por qué haces todo esto si solo son unos pocos kilobytes?». Repito: lo que estamos buscando es que, si esto crece a medio y largo plazo con muchísimos componentes nuevos que ahora mismo no podemos imaginar —pongo un ejemplo simple: enlaces externos, recursos a libros, etcétera—, realmente no nos suponga un coste elevado a nivel técnico. Vamos a ver cómo implementamos esta estrategia de dos maneras. La primera es con switch, case y defer. Partimos de la base de que tenemos un vector de elementos donde cada uno de ellos puede ser un párrafo, un vídeo, un recurso externo, un consejo, etcétera. Vamos a ir recorriendo cada posición de ese vector y, por cada uno de esos bloques, vamos a ir preguntando de qué tipo es: si es de tipo header entramos en esta rama, si es de tipo párrafo entramos en esta otra, y así sucesivamente. Pero esto no es suficiente para que se descargue bajo demanda: para eso lo que tenemos que hacer es poner defer. Esto lo que hace es que solo cuando entramos en esta línea de código los componentes que están dentro se van a pedir al servidor; si no entramos nunca en esa rama, nunca se va a descargar ese componente. Vamos a ver la solución de manera práctica. Aquí tenemos un botón que va a mostrar solo un párrafo y un header, y va a seguir esta estrategia. Lo que podemos ver es que, si pulsamos, los únicos elementos que se han mostrado son un header con su título y un párrafo, que es exactamente lo único que se ha descargado: un header y un párrafo; los otros chunks son otras cosas. Además, si pulsamos el botón «añadir tabla», en ese momento es cuando se descarga el componente de tipo tabla, no antes. Por lo que vemos, se cumple una de las desventajas de esta estrategia: si en el futuro queremos añadir nuevos bloques de teoría especiales para chino, para ruso, para alemán o para lo que sea, será muy difícil de mantener, porque imaginaos que esto crecerá muchísimo. Vamos a ver otra estrategia un poco más decente. La siguiente estrategia es un poco más limpia: solo tenemos este HTML y un bucle for que, por cada tipo de bloque, va imprimiendo lo mismo que antes. La clave es el ngComponentOutlet, que va a ir renderizando componentes según el tipo en cada iteración del bucle. Aquí lo más importante es que tenemos un vector de componentes donde cada componente es de tipo imagen, de tipo texto con imagen, de tipo encabezado, etcétera, y que cada componente se carga con una promesa: es decir, no lo precargamos, sino que directamente lo dejamos como algo asíncrono que se cargará en el futuro. Con esto tenemos una manera muy extensible de ir añadiendo diferentes tipos de componentes teóricos y que automáticamente se añadan a todo el ecosistema. Otra ventaja de esta estrategia es que podemos obligar con TypeScript a que, si añadimos un nuevo tipo de bloque, siempre tenga que ir en este array, y así no nos olvidamos de añadirlo por ejemplo en una plantilla. Más adelante veremos en otros vídeos cómo aprovechar TypeScript para que, siempre que añadamos un nuevo tipo de bloque teórico, cumplamos con la implementación de unas funciones y así no nos quedemos a medias; es decir, que no nos olvidemos de añadirlo en toda la plataforma, sino que el propio tipado nos diga: «oye, tienes que añadirlo aquí, aquí y aquí». Sobre todo, lo que buscamos en este vídeo es ver la diferencia entre implementar una solución que a medio y largo plazo no va a escalar muy bien y una solución que sí va a escalar; y además aprovechamos para enseñar cómo cargar componentes de manera óptima, intentando siempre reducir la descarga bajo demanda. Recordemos que no todo el mundo, es decir, no todos los países, tienen una conexión a internet superrápida, por lo que cosas que para nosotros pueden parecer tonterías —un par de kilobytes de diferencia— en otros países sí que pueden suponer algo que se nota a simple vista. Por eso siempre tenemos que intentar hacer aplicaciones eficientes y óptimas. Espero que te haya gustado el vídeo y, si tienes cualquier duda, puedes escribirme al correo de aquí debajo; en el caso de que haya mucha demanda puedo crear un vídeo especial para esa duda y, si no, respondértela de manera particular. Así que nada, nos vemos en otros vídeos.