¿Cómo mejorar el rendimiento de Angular aplicaciones?
¿Cómo mejorar el rendimiento de Angular aplicaciones? Lea esta publicación de blog para encontrar las formas, desde reducir JavaScript y CSS no utilizados hasta Angular la carga diferida.
Angular ha convertido en un marco muy popular y ampliamente adoptado para el desarrollo de aplicaciones web modernas. Esta tecnología es muy potente y rica en funciones. Todo lo que necesita como desarrollador web viene listo para usar y Angular permite configurar, mantener y expandir fácilmente cualquier aplicación construida sobre el marco.
Y a estas alturas, probablemente ya hayas reunido una o más aplicaciones Angular, pero ¿son óptimas?
A continuación, en la Parte 2 de mi serie Rendimiento del software, hablaré sobre la optimización de Angular, demostrando cómo mejorar el rendimiento de una aplicación de Angular utilizando una aplicación de ejemplo Angular que he creado. Usaré Chrome Dev Tools para derivar una puntuación de faro inicial para determinar dónde se encuentra inicialmente la aplicación. Echemos un vistazo a lo que se puede mejorar y cómo.
Otros blogs de esta serie:
Rendimiento del software [Web] Parte I
Cómo mejorar el rendimiento de Angular aplicaciones
Para este artículo, usaré un ejemplo de aplicación Angular que he reunido. En el momento de escribir este artículo, la aplicación utiliza las siguientes características y bibliotecas:
- Angular 16
- Ignite UI for Angular 16
- RxJS 7
- PWA (Angular service worker)
- Server-side rendering (express server)
- Angular localization
Angular Build
Todo parece funcionar bien cuando estoy ejecutando la aplicación en un entorno de desarrollo, pero la puntuación inicial del faro no es muy alta:

Cuandomirolas sugerencias para mejorar las secciones con menor puntuación, veo de dónde vienen los problemas. Elprimer gran problema es el tamaño de los recursos (JavaScript, estilos, recursos estáticos) que se transfieren al cliente.

Este problema se resuelve fácilmente ejecutando una compilación de producción de mi aplicación Angular en lugar de una de desarrollo. Construya siempre con la configuración de producción antes de la implementación. Esto resolverá la advertencia para reducir el tamaño deJavaScriptyCSS.Echemos un vistazoal archivo angular.json en la raíz de nuestrorepositorio A ngular para ver en qué se diferencia la compilación de producción:
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "10kb"
}
],
"fileReplacements": [
{
"replace": "projects/common/src/environments/environment.ts",
"with": "projects/common/src/environments/environment.prod.ts"
}
],
"localize": true,
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"serviceWorker": true,
"i18nMissingTranslation": "error",
"ngswConfigPath": "projects/bellumgens/src/ngsw-config.json"
},
"bg": {
"localize": [
"bg"
]
}
}
There’s quite a lot of configuration here. However, the most important one, in this case, is the "optimization": true one. Once I run the application with a production configuration, the difference in score is significant in terms of load-time performance:

Si vuelvo a mirar la lista de oportunidades, el número de sugerencias es mucho menor. Lasmayores oportunidades enumeradas en Acerca de la compresión de textosonJavaScript no utilizado y el almacenamiento en caché de recursos estáticos:

Angular Pre-Rendering and Server-Side Rendering
Angular es un marco de aplicación de una sola página (SPA). De forma predeterminada, el ciclo de vida de la aplicación es tal que, a petición de un cliente, el servidor que aloja la aplicación sirve un archivo HTML que incluye todas las referencias de estilo y script necesarias. Sin embargo, está vacío de contenido corporal. Una vez que se solicitan y sirven los scripts y estilos, la aplicación se arranca y se rellena con contenido basado en la lógica de JavaScript para la aplicación determinada. Angular proporciona dos mecanismos para mejorar este ciclo de vida mediante la publicación de contenido real en el documento HTML inicial. Para ello, la lógica de JavaScript de la aplicación debe ejecutarse antes de entregar el documento. Las formas de hacerlo:
- Ya sea en tiempo de compilación (pre-renderizado), para páginas con contenido mayoritariamente estático.
- O en tiempo de ejecución en el servidor (renderizado del lado del servidor): para páginas con contenido más dinámico que necesitan entregar contenido actualizado en cada solicitud.
He habilitado la representación del lado del servidor para la aplicación de ejemplo Angular y estoy usando el motor exprés para habilitar la compresión de texto y el almacenamiento en caché de recursos estáticos. Esto se hace agregando lo siguiente a mi lógica de servidor express:
export const app = (lang: string) => {
// server scaffolded by [ng add @nguniversal/express-engine]
...
// enable compression [npm install compression]
const compression = require('compression');
server.use(compression());
...
// Serve static files from /browser with 1y caching
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
...
}
Serviré la aplicación con renderizado del lado del servidor y volveré a ejecutar la prueba del faro. La carga inicial ha mejorado aún más, llevando la primera pintura contenta a menos de un segundo, mientras que el índice de velocidad se reduce a 1,2 segundos.

Las oportunidades restantes para la optimización de Angular son reducir JavaScript y CSS no utilizados.

Para lidiar con estos, tendré que hacer una refactorización de aplicaciones.
Angular Lazy-Loading
Para reducir el JavaScript no utilizado, el mejor enfoque sería crear rutas de carga diferida. Esto le dirá al marco de Angular qué componentes no son necesarios en el módulo de nivel superior y dividirá el JavaScript en módulos, cuya lógica se carga solo cuando se carga la ruta solicitada.
La aplicación de ejemplo Angular que estoy usando para este blog utiliza componentes más grandes, como igx-grid, que no forman parte de la ruta de inicio. Estoy separando las rutas usando este componente en un módulo separado. De esta manera, voy a tener ese componente cargado solo después de que se carguen las rutas que usan el componente. Después de separar los módulos, las rutas se ven así:
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'register', component: RegistrationComponent },
{ path: 'unauthorized', redirectTo: 'unauthorized/', pathMatch: 'full' },
{ path: 'unauthorized/:message', component: UnauthorizedComponent },
{ path: 'emailconfirm', component: EmailconfirmComponent },
{ path: 'strategies', loadChildren: () => import('./strategies/strategies.module').then(m => m.StrategiesModule) },
{ path: 'emailconfirm/:error', component: EmailconfirmComponent },
{ path: 'players', loadChildren: () => import('./player-section/player.module').then(m => m.PlayerModule) },
{ path: 'team', loadChildren: () => import('./team-section/team.module').then(m => m.TeamModule) },
{ path: 'notifications', loadChildren: () => import('./notifications/notifications.module').then(m => m.NotificationsModule) },
{ path: 'search/teams/:query', component: TeamResultsComponent },
{ path: 'search/players/:query', component: PlayerResultsComponent },
{ path: '**', component: HomeComponent }
];
The team.module is the one loading the grid I’m using, so the code for it looks like this:
@NgModule({
imports: [
CommonModule,
FormsModule,
// ...
IgxGridModule,
// ...
TeamComponent,
// ...
]
})
export class TeamModule { }
En cuanto a la construcción, este es el resultado de la división inicial:

Otra cosa que hay que hacer para mejorar el rendimiento de la aplicación Angular es limitar el tamaño del CSS a los estilos que se utilizan. Estoy usando Ignite UI for Angular como mi biblioteca de interfaz de usuario y hay un excelente artículo instructivo sobre cómo personalizar y optimizar los temas de los componentes.
Optimizing Images
Otro aspecto de la optimización de la Angular que se debe realizar es la optimización de las imágenes. Lighthouse check te dirá si estás utilizando imágenes subóptimas por tipo o tamaño. Asegúrese de que las imágenes que cargue no sean más grandes que los contenedores en los que van y que tengan una codificación óptima. Ahora uso el formato .webp para las imágenes. Reduce un poco la calidad, pero, aún así, la aplicación no requiere imágenes de la más alta fidelidad.
Las imágenes provocan cambios de diseño después de cargarlas. Por lo tanto, es una buena idea establecer los atributos de ancho y alto en las imágenes para que el navegador conozca las dimensiones antes de cargar las imágenes. Si te faltan ajustes de relación de aspecto (anchura y altura) en las imágenes, Lighthouse te avisará. Esto reducirá o eliminará por completo los cambios de diseño.
NgOptimizedImage to Enforce Image Best Practices
Angular exposes a directive that enforces image best practices and optimizes image loading for you. It’s called NgOptimizedImage and it is rather easy to use. All you need to do is import CommonModule if you’re still in an NgModule based Angular application, or import NgOptimizedImage in the component where you want to use it. Then, instead of using src to set the image source attribute, you use ngSrc instead.
<img ngSrc="/assets/wallpapers/strat-editor.webp" width="600" height="347" class="preview-image" alt="Strategy Editor" />
Por último, puedeespecificar aún más la prioridad de carga de cada imagen y decirle a la aplicación que cargue de forma diferida todas las imágenes no críticas. Si elimino los atributos de ancho y alto, lo que obtengo al ejecutar la aplicación es:

Y eso es todo.
Envolver...
Mejorar el rendimiento de Angular aplicaciones puede requerir supervisión continua, optimización y procedimientos recomendados para garantizar que la aplicación funcione de manera eficiente y confiable en diversas condiciones. Pero al final, así es como se ofrece la experiencia de usuario definitiva, se atrae y retiene a los usuarios, se mantiene la competitividad y se logra el éxito empresarial.
