Mejore el rendimiento del SPA dividiendo sus bibliotecas angulares en varias partes

Hola Habr! Le presento la traducción del artículo "Mejore el rendimiento de SPA dividiendo sus bibliotecas angulares en varios fragmentos" por Kevin Kreuzer .


Angular es un gran marco. Todos lo amamos <3.


Una de las cosas que hace que Angular sea exitosa y hermosa al mismo tiempo es la amplia comunidad y el valor que conlleva. Hay muchas reuniones, blogs, conferencias y, por supuesto, bibliotecas angulares.


Gracias a la CLI angular, las bibliotecas son fáciles de crear hoy en día. Son excelentes para compartir código entre múltiples aplicaciones.


Como pueden usarse en muchos lugares, el rendimiento es un aspecto crítico. ¡Una biblioteca que tiene un bajo rendimiento puede ralentizar múltiples aplicaciones!


Frontend tiene diferentes tipos de rendimiento. Tiempo de ejecución: rendimiento y carga inicial. En este artículo, nos centraremos en la carga inicial.


Al proporcionar y respaldar varias bibliotecas y marcos de interfaz de usuario para una gran empresa, me encontré con algunas dificultades y formas no tan obvias de solucionarlos. Creo que vale la pena compartir algunos de ellos.


"Esta es una biblioteca tan simple. No puede afectar el rendimiento, ¿verdad?"


Comencemos con una biblioteca simple que crearemos utilizando la CLI angular. Si nunca ha creado una biblioteca angular, podría serle útil leer el siguiente artículo:


imagen


Tan pronto como usemos la CLI para configurar gran parte del espacio de trabajo del proyecto, podemos comenzar a agregar código.


La biblioteca se llama howdy y su único propósito es saludarlo con su nombre o decirle la hora local. Contiene dos módulos con cada componente. Un modelo da la bienvenida, el otro habla el tiempo.


imagen


Solo un módulo angular y componente regular que toma una propiedad de nombre encima de los enlaces de entrada y lo muestra.


imagen


HowdyTimeComponent es responsable de mostrar el tiempo usando una biblioteca de momentos de terceros.


Genial ¡Nuestra biblioteca de Howdy está lista para su publicación! Es una biblioteca tan simple; ella no podrá afectar el rendimiento, ¿verdad?


Howdy consumo de biblioteca


¡Ahora tenemos una biblioteca! Sería una pena no aprovechar esto. Para utilizar la biblioteca de Howdy , estamos creando un nuevo SPA con la CLI angular.


ng new greeting-app 

Como estamos interesados ​​en el rendimiento, también instalemos la dependencia de desarrollo llamada webpack-bundle-analyzer .


 npm i -D webpack-bundle-analyzer 

Webpack-bundle-analyzer le permite visualizar el tamaño de los archivos de salida del paquete web mediante un mapa de árbol interactivo y escalable.


La mejor manera de analizar nuestro paquete es agregar el siguiente script de análisis a nuestro package.json .


 "analyze": "ng build --prod --stats-json && webpack-bundle-analyzer ./dist/greeting-app/stats-es2015.json" 

Si ejecutamos este comando, Angular realizará la compilación de producción y también generará stats-es2015.json , que luego será seleccionado y procesado por webpack-bundle-anlyzer .


imagen


Como todavía no hemos escrito ningún código, nuestro paquete principal consiste principalmente en Angular. También podemos ver que zone.js está incluido en nuestro paquete polyfill .


En general, el tamaño de nuestra aplicación ahora es de 207 KB .


¡Pero todavía no hemos incluido nuestra biblioteca de Howdy! Avancemos y hagámoslo.


 npm i howdy 

Instalamos la biblioteca de Howdy porque queremos alojar un saludo con un nombre. No nos interesa la demostración del tiempo. Por lo tanto, solo usaremos el módulo HowdyNameModule y no incluiremos HowdyTimeModule .


imagen


Es importante tener en cuenta aquí que solo importamos HowdyNameModule . Ejecutemos el script de análisis de análisis nuevamente.


imagen


Wow! Muy guay! Cambiamos de 207 KB a 511.15 KB. El tamaño se ha más que duplicado. ¡Qué ...!


Una mirada es suficiente para encontrar al culpable. ¡El momento es enorme! Trae su código de implementación principal y todas las configuraciones regionales.


Por supuesto, el momento se puede reemplazar con otros paquetes, como date-fns o moment-mini . Pero la pregunta es diferente; ¿Por qué está él allí? Recuerde que solo importamos HowdyNameModule , no HodwyTimeModule . Pensé que cuando se produce la sacudida del árbol , ¿solo se sacuden los módulos no utilizados? Que esta pasando


La sacudida de los árboles puede no eliminar todo


Para que ocurra la sacudida del árbol , la compilación angular lanza un montón de optimizaciones avanzadas. Pero aún así, el momento está presente en el kit, aunque HowdyTimeModule no lo está .
El problema es cómo se empaqueta el momento. Echemos un vistazo rápido al archivo moment.js en nuestra carpeta node_modules .


imagen


Dado que el momento se puede usar en muchos lugares, como backends de Node JS, aplicaciones angulares o JavaScript ordinario, se incluye en UMD y no como un módulo ES .


Las bibliotecas UMD vinculadas están envueltas en una función IFFE, lo que significa que no se puede usar la Concatenación de módulos . Las herramientas de optimización de ensamblaje no pueden determinar si se usará este código o si tiene efectos secundarios.


En pocas palabras, este tipo de módulo evita que Angular lance un kit de optimización más avanzado.


Desafortunadamente, no podemos controlar cómo se completa el momento . ¿Esto significa que tenemos que soportar el tamaño del paquete?


Puntos de entrada secundarios para la victoria.


No podemos controlar cómo se crea el momento . Pero podemos administrar nuestra biblioteca. De hecho, hay una manera de prevenir tales escenarios. Puntos de entrada secundarios!


Casi todas las bibliotecas angulares se empaquetan actualmente usando ng-packagr . ng-packagr le permite usar ng-package.json en combinación con public-api , que eventualmente se convertirá en el punto de entrada a su aplicación.


Como su nombre lo indica, los puntos de entrada adicionales le permiten especificar múltiples puntos de entrada para su aplicación.


Eso suena bien! ¿Cómo activar los puntos de entrada secundarios?


Los puntos de entrada secundarios se detectan dinámicamente usando ng-packagr . ng-packagr busca archivos package.json en subdirectorios de la carpeta principal del archivo package.json


Genial! Aprovechemos los puntos de entrada secundarios en nuestra biblioteca howdy agregando los siguientes archivos.


imagen


Para cada módulo, agregamos index.ts , package.json y public_api.ts .


  • index.ts está ahí, solo para señalar public_api , que es útil durante la importación.
  • public_api exporta todos los módulos y componentes de nuestro módulo.
  • package.json contiene configuraciones específicas de ng-packagr . En nuestro caso, esto es suficiente para especificar entryFile .

Package.json también puede contener otras propiedades, como cssUrl , etc. Tenga en cuenta que el alcance de estas propiedades es solo el subelemento actual.

Si ejecutamos el ensamblaje ahora, obtenemos tres bloques. howdy.js , howdy-src-lib-name.js y howdy-src-lib-time.js .


Howdy-src-lib-name.js ahora solo contiene código relacionado con HowdyNameModule , y howdy-src-lib-time.js ahora solo contiene código específico de HowdyTimeModule .


Pero echemos un vistazo a un pedazo de howdy.js .


imagen


La pieza howdy.js todavía contiene HowdyNameComponent y HowdyTimeComponent . Esto significa que todavía tenemos el momento, incluso si importamos solo HowdyNameModule .


Si queremos deshacernos de HowdyTimeModule con este enfoque, debemos usar la importación profunda. Así que no importamos desde howdy.js , sino directamente desde howdy-src-lib-time.js
Lo que no se recomienda! ¡Las importaciones profundas son peligrosas y siempre deben evitarse!

¿Cómo podemos resolver estos problemas? ¿Cómo podemos garantizar que el HowdyTimeModule también se eliminará incluso si usamos importaciones estándar? Bueno, necesitamos establecer una forma de crear una pieza de howdy.js .


Utilice "señal"


La idea es eliminar el código del bloque howdy.js y, en su lugar, permitir que actúe como una especie de "puntero" de "poste indicador" que lo señala a otros bloques.


Así que echemos un vistazo más de cerca a src / public_api.ts .


 /* * Public API Surface of howdy */ export * from './lib/name/howdy-name.component'; export * from './lib/name/howdy-name.module'; export * from './lib/time/howdy-time.component'; export * from './lib/time/howdy-time.module'; 

Estas líneas son responsables de incluir todo, desde el nombre y la hora en el bloque howdy.js . Necesitamos eliminar el código de howdydy.js y dejar que apunte a otros fragmentos que contengan la implementación. Cambiemos su contenido.


 / * Public API Surface of howdy */ export * from 'howdy/src/lib/name'; export * from 'howdy/src/lib/time'; 

En lugar de exportar la implementación real, indicamos la ruta relativa a las diferentes partes. Con este cambio, howdy.js solo apunta a otros paquetes y no contiene ningún código "real".
Ejecutemos ng build y analicemos nuestra carpeta dist .


imagen


Howdy.js ahora actúa como un "indicador" "indicador" que apunta a fragmentos que contienen la implementación. El bloque howdy-src-lib-name.js contiene solo el código de la carpeta de nombres , y el archivo howdy-src-lib-time.js contiene solo el código de la carpeta de tiempos .


Completa el paquete con sustitutos


Actualicemos el paquete howdy en nuestra aplicación de bienvenida y vuelva a ejecutar el script de análisis.


imagen


Genial El tamaño del paquete ahora es de 170,94 KB . Ligeramente más alto que originalmente. Veamos cómo se ve el módulo Howdy en el paquete final.


imagen


Genial Este ajuste nos permite mantener pequeño el tamaño del paquete que consume SPA. ¡SPA obtiene solo lo que necesitan!


Los puntos de entrada secundarios son muy buenos cuando los usa junto con la carga diferida. Si usáramos HowdyTimeModule en un módulo con carga lenta , el momento terminaría en una pieza con carga lenta, y no básicamente.

Experiencia real


El ejemplo anterior es muy simple.


Sin embargo, después de la introducción de puntos de entrada secundarios en el proyecto corporativo existente, todo será diferente. Tiene que lidiar con una complejidad mucho mayor, mientras que los mensajes de error de ng-packagr no siempre son útiles.


Lo más probable es que deba configurar algunas rutas de importación o especificar algunas rutas en su archivo tsconfig.json . Y también encontrará módulos de un bloque que usan módulos de otro bloque.


Pero créeme, una vez que manejas esta carga, vale la pena.


En un entorno corporativo extenso, descubrimos que el tamaño del paquete del SPA recién creado explotó después de incluir algunas de las bibliotecas que proporcionamos.


En algún momento, incluso subió a 5 MB . Cada SPA recibió un momento , @swimlane / datatable y otras cosas que ni siquiera usó. Comenzamos a centrarnos en optimizar el tamaño de este paquete.


Eliminamos el momento de date-fns y comenzamos a usar puntos de entrada secundarios. Actualmente, hemos recibido una unidad principal de 662 KB para el recién creado SPA, que incluye varias bibliotecas. Esto todavía es mucho, pero aún no hemos terminado. La optimización aún no está completa: podemos reducir el tamaño del paquete aún más.


Es genial ver dónde estamos y de dónde venimos.


Sin embargo, a pesar de que en el ejemplo anterior es bastante fácil usar puntos de entrada adicionales, puede ser bastante difícil imaginarlos en un proyecto más significativo.


Conclusión


Angular hace un trabajo maravilloso cuando se trata de optimizar el tamaño del paquete. Aunque los pasos de montaje para la optimización son muy complejos, no pueden sacudir la sacudida del árbol.


Los módulos empaquetados en formatos que no sean ESModules no se pueden sacudir en árbol.


Por lo tanto, como creadores de bibliotecas, debemos monitorear cuidadosamente el impacto de nuestra biblioteca en el tamaño del paquete cuando incluimos bibliotecas de terceros.
No podemos controlar el empaquetado de bibliotecas de terceros. Pero tenemos muy buen control sobre el embalaje de nuestra biblioteca.


Las subpartes nos ofrecen una excelente manera de entregar nuestra biblioteca en varias partes. Estas piezas se pueden sacudir (sacudible de árbol) durante la optimización del ensamblaje de los angulares. Con este enfoque, incluso las bibliotecas de terceros empaquetadas incorrectamente se incluyen en el paquete final solo si se usan.

Source: https://habr.com/ru/post/482646/


All Articles