DEV Community

Cover image for Usar Lazy load en componentes Angular
Antonio Cardenas for Macao And Friends Blog

Posted on • Updated on • Originally published at Medium

Usar Lazy load en componentes Angular

con ivy en Angular聽9

Lazy loading en componentes Angular? 馃Quiz谩s quieras decir m贸dulos lazy loading con el router de Angular!

隆No, lo has le铆do correctamente , lazy loading en componentes!

Si, la versi贸nes pre ivy de Angular solo soportan lazy loading de m贸dulos . Pero Ivy abre un nuevo mundo de posibilidades.

Lazy loading hasta ahora鈥 Rutas Lazy loaded

El Lazy loading es una gran caracter铆stica. En Angular, lo obtenemos casi gratis al declarar una ruta lazy.

Lazy loading a module in Angular / source: angular.io

El c贸digo anterior generar铆a un fragmento separado para el customers.module que se carga tan pronto como llegamos a la ruta de customer-list.
Es una forma muy bonita de reducir el tama帽o de su paquete principal y aumentar la carga inicial de su aplicaci贸n.
A煤n as铆, 驴no ser铆a genial si tuvi茅ramos un control a煤n m谩s granular sobre el lazy loading? Por ejemplo, 驴mediante el lazy loading de componentes individuales?
El lazy loading de componentes individuales no ha sido posible hasta ahora. Pero las cosas han cambiado con Ivy.

馃尡 Ivy introduce 鈥渓ocalidad鈥.

Los m贸dulos son un concepto de primera clase y el componente principal de todas las aplicaciones de Angular. Declaran varios componentes, directivas, pipes y servicios.

Las aplicaciones Angular de hoy no pueden existir sin m贸dulos. Una de las razones de esto es que el ViewEngine agrega todos los metadatos necesarios a los m贸dulos.

Ivy, por otro lado, adopta otro enfoque. En Ivy, un componente puede existir sin un m贸dulo. Gracias a un concepto llamado 鈥淟ocalidad鈥.

"Localidad" significa que todos los metadatos son locales al componente.

Perm铆tanme explicar esto echando un vistazo m谩s de cerca a un paquete es2015 generado con Ivy.

es2015 bundle generated with Ivy 馃尡

En la secci贸n "C贸digo de componente", podemos ver que Ivy mantiene nuestro c贸digo de componente. Nada especial. Pero luego Ivy tambi茅n le agrega algunos metadatos.

El primer metadato que agrega es un Factory que sabe c贸mo instanciar nuestro componente ("Component Factory"). En la parte de "Metadatos de componentes", Ivy agrega m谩s atributos como type, selector, etc., todo lo que necesita en tiempo de ejecuci贸n.

Una de las cosas m谩s interesantes que agrega Ivy es la funci贸n de template. Lo que merece algunas explicaciones adicionales.

La funci贸n de plantilla es la versi贸n compilada de nuestro HTML. Ejecuta las instrucciones de Ivy para crear nuestro DOM. Esto difiere de la forma en que funcionaba ViewEngine.

El ViewEngine tom贸 nuestro c贸digo y lo repiti贸. Angular estaba ejecutando c贸digo si lo est谩bamos usando.

Con el enfoque Ivy, el componente est谩 en el asiento del conductor y ejecuta Angular. Este cambio permite que un componente viva por s铆 solo y hace que el n煤cleo angular se le pueda aplicar tree-shaking

Un ejemplo del mundo real de un componente Lazy聽Loading

Un ejemplo del mundo real de carga diferida de un componente
Ahora que sabemos que la carga diferida es posible, lo demostraremos en un caso de uso del mundo real. Vamos a implementar una aplicaci贸n Quiz.
La aplicaci贸n muestra una imagen de la ciudad con diferentes posibles soluciones. Una vez que un usuario elige una soluci贸n, el bot贸n en el que se hace clic muestra inmediatamente si la respuesta fue correcta o no volvi茅ndose rojo o verde.
Despu茅s de responder una pregunta, aparece la siguiente pregunta. Aqu铆 tienes una vista previa r谩pida:

El concepto de de un componente lazy loading馃懆鈥嶐煄

Primero, ilustremos la idea general de la carga diferida de nuestro componente QuizCard.

Una vez que el usuario inicia el quiz haciendo clic en el bot贸n "Start Quiz", comenzamos a cargar nuestro componente usando lazy load. Una vez que tengamos el componente, lo agregaremos a un contenedor.

Reaccionamos a los eventos de salida questionAnwsered de nuestro componente lazy-loaded como lo hacemos con los componentes est谩ndar. Una vez que ocurre el evento de salida questionAnwsered, agregamos una nueva tarjeta Quiz.

Entendido, echemos un vistazo al c贸digo聽馃攳

Para explicar el proceso de carga diferida de un componente, comenzaremos con una versi贸n simplificada de nuestro QuizCardComponent que muestra de manera simplista las propiedades de la pregunta.

Luego, ampliaremos nuestro componente agregando componentes de Material angular. Por 煤ltimo, pero no menos importante, reaccionamos a los eventos de salida de nuestro componente de carga diferida.

Entonces, por ahora, carguemos una versi贸n simplificada del QuizCardComponent que tiene la siguiente plantilla:

El primer paso es crear un elemento contenedor. Para esto, usamos un elemento real como un div o podemos usar un ng-container, que no introduce un nivel extra de HTML.

Genial, tenemos el contenedor donde queremos agregar nuestro componente lazy-loaded. A continuaci贸n, necesitamos un ComponentFactoryResolver y un Injector que podemos utilizar ambos mediante inyecci贸n de dependencia.

Un ComponentFactoryResolver es un registro simple que asigna componentes a clases de ComponentFactory generadas que se pueden usar para crear instancias de componentes.

Ok, en este punto, tenemos todas las cosas que necesitamos para lograr nuestro objetivo. Cambiemos nuestro m茅todo startQuiz y carguemos nuestro componente de manera lazy load.

Podemos usar la funci贸n import de ECMAScript para usar lazy load en nuestro QuizCardComponent. La declaraci贸n de importaci贸n nos devuelve una promesa que manejamos usando async/await o con un controlador then. Una vez que la promesa se resuelve, usamos la desestructuraci贸n para hacer grep al componente.

No use async/await cuando compile en es2017. Zone js no puede parchear declaraciones nativas async/await. Por lo tanto, puede tener problemas con la detecci贸n de cambios. Si compila su c贸digo en es2017, debe usar un controlador聽.then con una funci贸n de devoluci贸n de llamada.

Para ser compatible con versiones anteriores, hoy en d铆a necesitamos un ComponentFactory. Esta l铆nea no ser谩 necesaria en el futuro ya que podemos trabajar directamente con el componente.

El ComponentFactory nos da un componentRef que luego, junto con el Injector, pasaremos al m茅todo createComponent de nuestro contenedor.

El createComponent nos devuelve un ComponentRef que contiene una instancia de nuestro componente. Usamos esta instancia para pasar las propiedades de @Input a nuestro componente.

En el futuro, todo esto podr铆a hacerse utilizando el m茅todo renderComponent de Angular. Este m茅todo todav铆a es privado/experimental. Sin embargo, es muy probable que este m茅todo llegue a Angular. Lars Gyrup Brink Nielsen dio un gran taller sobre esto en InDepthConf.

Eso es todo lo que se necesita para cargar un componente usando lazy load.

Una vez que se hizo clic en el bot贸n de inicio, cargamos nuestro componente usando lazy load. Si abrimos la pesta帽a de red, podemos ver que el fragmento quiz-card-quiz-card-component.js a sido cargado mediante lazy load. En la aplicaci贸n en ejecuci贸n, se muestra el componente y se muestra la Pregunta.

Agreguemos material聽馃懛

Actualmente, cargamos nuestro QuizCardComponent mediante lazy load. Muy genial. Pero nuestra aplicaci贸n a煤n no es 煤til.

Cambiemos eso agregando caracter铆sticas adicionales y algunos componentes de Angular material.

Incluimos algunos hermosos componentes de Material. Pero, 驴d贸nde agregamos los m贸dulos de material?

S铆, podr铆amos agregarlos a nuestro AppModule. Pero, esto significa que esos m贸dulos se cargan ansiosamente (eagerly loaded). Entonces esa no es la mejor soluci贸n. Adem谩s, nuestra compilaci贸n falla con el siguiente mensaje:

ERROR in src/app/quiz-card/quiz-card.component.html:9:1 - error TS-998001: 'mat-card' is not a known element:
1. If 'mat-card' is an Angular component, then verify that it is part of this module.
2. If 'mat-card' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
Enter fullscreen mode Exit fullscreen mode

驴Ahora que? Como puedes imaginar, existe una soluci贸n a este problema. 隆Y la respuesta son m贸dulos!
Pero esta vez los usaremos de manera ligeramente distinta. Agregamos un peque帽o m贸dulo al mismo archivo que nuestro QuizCardComponent.

Esta especificaci贸n de m贸dulo solo pertenece a nuestro componente lazy loaded. Por lo tanto, el 煤nico componente que este m贸dulo declarar谩 es el QuizCardComponent. En la secci贸n de imports, solo agregamos los M贸dulos necesarios para nuestro componente.
Para asegurarnos de que un m贸dulo cargado ansiosamente (eagerly loaded) no pueda importar el m贸dulo, no lo exportamos.
Volvamos a ejecutar nuestra aplicaci贸n y veamos c贸mo se comporta cuando hacemos clic en el bot贸n "Start Quiz".

隆Incre铆ble! Nuestro QuizCardComponent se carga de manera lazy loaded y se agrega al ViewContainer. Tambi茅n trae todas las dependencias necesarias.

Usemos una herramienta llamada webpack-bundle-analyzer y analicemos el aspecto del paquete.

webpack-bundle-analyzer es un m贸dulo npm que le permite visualizar el tama帽o de los archivos de salida del paquete web con un mapa de 谩rbol interactivo con zoom.

El tama帽o de nuestro paquete principal es de alrededor de 260 KB. Si carg谩ramos de manera ansiosamente (eagerly loaded) el, QuizCardComponent ser铆a de alrededor de 270 KB. Ahorramos alrededor de 10 KB cargando de forma diferida solo este componente. 隆Muy genial!

Nuestro QuizCardComponent se incluy贸 en un bloque separado. Si echamos un vistazo m谩s de cerca al contenido de este fragmento, no solo encontramos nuestro c贸digo QuizCardComponent, sino que tambi茅n vemos los m贸dulos Material utilizados dentro del QuizCardComponent.

Aunque QuizCardComponent us贸 MatButtonModule y MatCardModule, solo MatCardModule termina en el fragmento del componente de la tarjeta de prueba. La raz贸n de esto es porque tambi茅n usamos MatButtonModule en nuestro AppModule para mostrar el bot贸n de inicio de prueba. Por lo tanto, termina en otro trozo.

En este punto, cargamos de manera lazy-loaded nuestro QuizCardComponent, que muestra una hermosa tarjeta de Material con una imagen y algunas posibles respuestas. Pero, 驴sucede actualmente si hace clic en una de esas posibles respuestas?

Seg煤n su respuesta, el bot贸n se vuelve verde o rojo. 驴Pero adem谩s de eso? 隆Nada! Entonces ahora se muestra otra pregunta. Arreglemos eso.

Reaccionar ante eventos de componentes con lazy聽loading

No se muestran m谩s preguntas porque a煤n no reaccionamos al evento de salida de nuestro componente lazy-loaded. Ya sabemos que nuestro QuizCardComponent emite eventos usando un EventEmitter. Si miramos la definici贸n de clase de EventEmitter, podemos ver que EventEmitter hereda de Subject.

export declara la clase EventEmitter <T extiende cualquiera> extiende Subject <T>
Enter fullscreen mode Exit fullscreen mode

Significa que EventEmitter tambi茅n tiene un m茅todo de suscripci贸n, que nos permite reaccionar a los eventos emitidos.

Nos suscribimos al flujo questionAnswered y llamamos al m茅todo showNextQuestion, que luego ejecuta nuestra l贸gica lazyLoadQuizCard.

takeUntil(instance.destroy$) es necesario para limpiar la suscripci贸n una vez que el componente se destruya. Si se llama al gancho del ciclo de vida (lifecycle hook) ngOnDestroy de QuizCardComponent, se llama a destroy$ el Subject es llamado con next y complete.

async showNewQuestion() {
  this.lazyLoadQuizCard();
}
Enter fullscreen mode Exit fullscreen mode

Dado que la QuizCard ya se carg贸, no se ha realizado ninguna solicitud HTTP adicional. Usamos el contenido del fragmento cargado previamente, creamos un nuevo componente y lo agregamos a nuestro contenedor.

Ganchos de ciclo de vida(Life cycle聽hooks)

Casi todos los enganches del ciclo de vida se llaman autom谩ticamente si cargamos de manera lazy load nuestro QuizCardComponent. Pero falta un gancho, 驴ves cu谩l?

Es el primero de todos los ganchos, ngOnChanges. Dado que actualizamos manualmente las propiedades de entrada de nuestra instancia de componente, tambi茅n somos responsables de llamar al enlace del ciclo de vida ngOnChanges.

Para llamar a ngOnChanges en la instancia, necesitamos construir manualmente el object SimpleChanges.

Llamamos manualmente a ngOnChanges en nuestra instancia de componente y le pasamos un objeto SimpleChange. El SimpleChange indica que es el primer cambio, que el valor anterior era null y que el valor actual es nuestra pregunta.

隆Incre铆ble! Cargamos de forma diferida un componente con m贸dulos de terceros, reaccionamos a los eventos de salida y llamamos a los enlaces correctos del gancho ciclo de vida(life cycle hooks).

驴Te Interesa el c贸digo聽fuente?

Todas las fuentes utilizadas a lo largo de esta publicaci贸n de blog est谩n disponibles p煤blicamente en el siguiente repositorio.

https://github.com/kreuzerk/city-quiz

Conclusi贸n

El componente mediante Lazy loading ofrece grandes posibilidades para optimizar a煤n m谩s nuestra aplicaci贸n en lo que respecta al rendimiento. Tenemos un control m谩s granular de lo que queremos cargar en forma diferida en comparaci贸n con las funciones de carga diferida con el enrutador angular.

Desafortunadamente, todav铆a necesitamos m贸dulos cuando usamos otros m贸dulos en nuestro componente. Tenga en cuenta que es muy probable que esto cambie en el futuro.

Ivy usa la localidad, lo que permite que los componentes vivan por s铆 mismos. Este cambio es la base para el futuro de Angular.

馃鈥 馃檹 Si te gust贸 esta publicaci贸n, comp谩rtela y aplaude馃憦馃徎 haciendo clic varias veces en el bot贸n de aplaudir en el lado izquierdo.

Los aplausos ayudan a otras personas a descubrir contenido y me motivan a traducir m谩s art铆culos 馃槈

Ng-sorting

https://www.npmjs.com/package/ng-sortgrid

Much铆simas gracias a Lars Gyrup Brink Nielsen y al autor Kevin Kreuzer de esta maravilloso articulo, ahora muchos art铆culos de Angular estar谩n en espa帽ol.

Articulo original si lo deseas ver en ingles聽
https://medium.com/angular-in-depth/lazy-load-components-in-angular-596357ab05d8

Top comments (0)