Angular Performance Optimisation
Sraban Pahadasingh June 24, 2024 11:07 AMList of performance optimization steps for Angular applications
1. Event Coalescing: {ngZoneEventCoalescing: true}
- Description: Aggregates multiple events into a single change detection run.
- Benefits: Reduces the number of change detection cycles.
2. Zone.js Optimization: {ngZone: 'noop'}
- Description: Disables Zone.js to manage change detection manually.
- Benefits: Provides fine-grained control over change detection, reducing unnecessary cycles.
3. OnPush Change Detection
- Description: Changes the default change detection strategy to
OnPush
, which only checks for changes when input properties change. -
Benefits: Limits change detection to specific component updates, improving performance.
@Component({ selector: 'app-my-component', templateUrl: './my-component.component.html', changeDetection: ChangeDetectionStrategy.OnPush })
4. Track By Function in ngFor
- Description: Uses a
trackBy
function inngFor
to identify items in a collection by a unique identifier. -
Benefits: Minimizes DOM re-renders by tracking changes more efficiently.
<div *ngFor="let item of items; trackBy: trackByFn"> {{item.name}} </div>
trackByFn(index: number, item: any): number { return item.id; // unique id corresponding to item }
5. Lazy Loading Modules
- Description: Defers loading of modules until they are needed, rather than loading all modules at startup.
-
Benefits: Reduces initial load time and improves application performance.
const routes: Routes = [ { path: 'feature', loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule) } ];
6. Preload Lazy-Loaded Modules
- Description: Uses Angular’s
PreloadAllModules
strategy to preload lazy-loaded modules after the initial load. -
Benefits: Balances initial load time with module availability.
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
7. AOT Compilation (Ahead of Time)
- Description: Pre-compiles the application at build time instead of runtime.
-
Benefits: Reduces application size and improves startup performance.
"angularCompilerOptions": { "enableIvy": true, "aot": true }
8. Tree Shaking
- Description: Removes unused code during the build process.
-
Benefits: Decreases the size of the final JavaScript bundle, leading to faster load times.
"optimization": { "minify": true, "removeUnusedDependencies": true }
9. Optimize Image Loading
- Description: Uses modern image formats (like WebP), lazy loading, and responsive images.
-
Benefits: Reduces the load on the network and improves rendering performance.
<img src="image.webp" loading="lazy" alt="description">
10. Code Splitting
- Description: Splits the application code into smaller chunks that are loaded on demand.
-
Benefits: Improves load times by only loading the necessary code for each route.
"optimization": { "splitChunks": { "chunks": "all" } }
11. Use Web Workers
- Description: Offloads heavy computation to background threads.
-
Benefits: Prevents blocking the main thread, maintaining a responsive UI.
if (typeof Worker !== 'undefined') { const worker = new Worker(new URL('./app.worker', import.meta.url)); worker.onmessage = ({ data }) => { console.log(`Page received message: ${data}`); }; worker.postMessage('hello'); }
12. Service Worker for Caching
- Description: Uses Angular’s service worker to cache application resources for offline access and faster load times.
-
Benefits: Reduces network requests and improves load performance.
import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production, registrationStrategy: 'registerWhenStable:30000' })
13. Use ng-container
to Group Elements
- Description: Uses
ng-container
to avoid unnecessary<div>
tags and reduce the size of the DOM tree. -
Benefits: Minimizes the number of elements in the DOM, leading to improved rendering performance.
<ng-container *ngIf="condition"> <p>Content</p> </ng-container>
14. Throttle or Debounce Event Handlers
- Description: Uses RxJS operators to limit the frequency of event handling.
-
Benefits: Reduces the number of function executions and subsequent change detection runs.
import { fromEvent } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; fromEvent(document, 'click') .pipe(debounceTime(300)) .subscribe((event) => console.log(event));
15. Memory Leak Prevention
- Description: Ensures subscriptions are properly unsubscribed and components are destroyed cleanly.
-
Benefits: Prevents memory leaks, which can degrade performance over time.
import { Subscription } from 'rxjs'; let subscription: Subscription = new Subscription(); ngOnDestroy() { subscription.unsubscribe(); }
16. Avoid Direct DOM Manipulation
- Description: Uses Angular’s templating system instead of direct DOM manipulation.
-
Benefits: Ensures changes are tracked by Angular's change detection, maintaining application consistency.
this.renderer.setStyle(element, 'display', 'none');
17. Defer and Async Loading for Scripts
- Description: Loads external scripts with
defer
orasync
attributes. -
Benefits: Prevents blocking the main thread during script loading, improving initial render times.
<script src="example.js" defer></script>
18. Minimize Use of *ngIf
and *ngFor
- Description: Reduces the use of
*ngIf
and*ngFor
directives to avoid frequent DOM changes. -
Benefits: Decreases change detection and re-rendering overhead.
<ng-container *ngIf="condition"> <!-- Conditional content here --> </ng-container>
19. Enable Production Mode
- Description: Ensures Angular runs in production mode, which disables development mode checks and optimizations.
-
Benefits: Reduces overhead and improves application performance.
import { enableProdMode } from '@angular/core'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); }
Configure tree shaking in angular app
Tree shaking is an optimization technique used to eliminate dead code (unused code) from a JavaScript bundle. In Angular applications, tree shaking is handled primarily by the build tool, which is usually Webpack, as part of the Angular CLI's build process. Here’s how you can ensure tree shaking is effectively implemented in your Angular application:
Key Steps to Ensure Tree Shaking in Angular Applications
-
Ensure Production Mode is Enabled
Angular's build process differentiates between development and production builds. Production builds enable optimizations like tree shaking, minification, and Ahead-of-Time (AOT) compilation.
How to Enable:
-
Via Command Line: Run your build command with the
--prod
flag:ng build --prod
-
In
angular.json
Configuration: Ensure yourproduction
configuration hasoptimization
enabled."configurations": { "production": { "optimization": true, "outputHashing": "all", "sourceMap": false, "extractCss": true, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true } }
-
-
Use ES6 Module Imports
Tree shaking works best with ES6 module syntax because the static structure of ES6 imports and exports makes it easier for the build tool to determine which code is used and which isn’t.
Example:
// Import only the required parts import { Component, OnInit } from '@angular/core';
-
Avoid Dynamic
require
StatementsAvoid using dynamic
require
statements, as they can prevent tree shaking. Always use static imports.Good:
import { MyModule } from './path/to/module';
Bad:
// Avoid dynamic imports that bring in unnecessary code const MyModule = require('./path/to/module'); const Component = require('@angular/core').Component; // Sometime import('large-module').then(module => { module.someFunction(); });
-
Optimize Angular CLI Configuration (
angular.json
)Ensure your
angular.json
file is set up to take advantage of the Angular CLI's optimizations.Configuration Snippet:
"optimization": { "scripts": true, "styles": true, "fonts": true }, "buildOptimizer": true,
-
Minimize Polyfills
Polyfills can add significant bloat to your application if not carefully managed. Only include necessary polyfills.
Example:
- Remove unnecessary polyfills from
polyfills.ts
.
- Remove unnecessary polyfills from
-
Use
sideEffects
inpackage.json
Specify that your code is side-effect free to help Webpack in identifying and eliminating dead code. The
sideEffects
field inpackage.json
indicates which files are safe to prune.Example:
{ "name": "your-app", "version": "1.0.0", "sideEffects": false }
-
Use Angular CLI's Tree Shakable Libraries
Angular CLI supports tree-shakable libraries. Ensure the libraries you use are designed to support tree shaking by checking their
package.json
for proper module fields likees2015
,module
, oresnext
. -
Optimize Third-Party Library Imports
Import only what you need from third-party libraries instead of the entire library.
Example:
// Good: Import only required parts import { debounceTime } from 'rxjs/operators'; // Bad: Import the whole library import * as _ from 'lodash';
-
Use AOT Compilation
AOT compilation precompiles the Angular application before the browser downloads and runs it. This helps reduce the bundle size and improve the runtime performance, indirectly aiding tree shaking.
How to Enable:
-
Via Command Line:
ng build --aot
- In
angular.json
:"aot": true
-
-
Review Webpack Configuration (Advanced)
Although Angular CLI abstracts Webpack configuration, advanced users might want to review and adjust Webpack settings directly to ensure tree shaking is optimized.
Example:
-
Custom Webpack configuration through Angular CLI builders like
@angular-builders/custom-webpack
."architect": { "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./extra-webpack.config.js" }, // other options... } } }
-
-
Monitor and Analyze Bundle Size
Regularly analyze your bundle size to ensure tree shaking is effectively reducing the application size. Tools like
source-map-explorer
orwebpack-bundle-analyzer
can be used for this purpose.Example:
npm install -g source-map-explorer source-map-explorer dist/your-app/main.*.js
Summary
To ensure effective tree shaking in your Angular application:
- Enable Production Mode in your build configuration.
- Use ES6 Module Imports and avoid dynamic requires.
- Optimize Angular CLI Configuration for tree shaking.
- Minimize Polyfills and specify side effects in
package.json
. - Leverage Tree Shakable Libraries and optimize third-party imports.
- Utilize AOT Compilation for pre-compilation benefits.
- Analyze Bundle Size to verify the effectiveness of tree shaking.
By following these steps, you can significantly reduce your application's bundle size, leading to faster load times and better performance.
Conclusion
Applying these performance optimization steps can significantly enhance the responsiveness, efficiency, and overall user experience of an Angular application. Each step targets specific aspects of application performance, from initial load times to runtime efficiency and resource management.