Make the most out of Angular’s performance.

Reduce your bundle size

If you don’t need to support old browsers, drop ES5 support. ES6 is already supported in all modern browsers.

Go to your tsconfig.json and change the “target” from es5 to es6 When building your app use --es5-browser-support=false with ng build to exclude es5 browser polyfills from your bundle or permanently set it to false from angular.json . Remove unused dependencies from your app. Make your Angular services tree-shakeable. Don’t import and add them to the providers array in your ngModule. instead use the providedIn attribute when decorating your service with @Injectable() to let it be included in the bundle only if its injected or used in any other components/services as follows

@Injectable({

providedIn: 'root'

})

export class MyService {}

5. Use Differential loading. if you’re using Angular version 8+ you should be able to use differential loading to avoid serving unneeded polyfills to modern browsers. simply add a browserslist file with the following content to your project and comment out all imports from src/pollyfills.ts except for zone.js, and make sure you have " target": “es2015" in your tsconfig.json.

> 0.5%

last 2 versions

Firefox ESR

Chrome 41 # Support for Googlebot

not dead

not IE 9-11 # For IE 9-11 support, remove 'not'.

6. Enable IVY https://next.angular.io/guide/ivy

Lazy loading & Code-splitting

With Angular its super easy to lazy-load modules based on routes

instead of

// app-routing.module.ts

const routes: Routes = [{ path: home', component: HomeComponent }, { path: 'about', component: AboutComponent

}, { path: '', redirectTo: 'home', pathMatch: 'full'

}]

use

// app-routing.module.ts

const routes: Routes = [{ path: home', component: HomeComponent }, { path: 'about', loadChildren: './about/about.module#AboutModule'

}, { path: '', redirectTo: 'home', pathMatch: 'full'

}]

Notice we’re not lazyloading the home component since its the default route, that is required for the initial load. Lazy-loading it will cause Angular to trigger another http request which will actually harm our app’s performance.

Use Service Workers to Cache assets

Service workers are an awesome technology that allows you to develop Progressive Web Apps (PWA), even if you don’t want to build a PWA you can use them to cache assets and HTTP requests for an almost instant load time. Angular makes this super easy to opt-in with the angular-cli.

All you have to do is

ng add @angular/pwa

Note: Service Workers can only be installed if your website supports HTTPS which you should be doing anyway.

Avoid Memory Leaks

Unsubscribe from your event listeners, observables and setIntervals in ngOnDestroy , otherwise they will stay in memory and will keep running even if your component is destroyed.

Debounce & Throttle your event listeners

If you have an event that gets fired frequently like an input change event or a scroll event, its better to debounce or throttle the event listener depending on the use-case to avoid running logic many times per frame affecting the FPS and performance of your app.

Memoize & Cache expensive functions

This is not really Angular specific, but if you have expensive functions that get called multiple times. you can always cache the result and return the result from the cache instead of computing the result again.

Minimize DOM Elements

instead of creating wrapper divs that you don’t need, or avoiding the problem of not being able to apply two directives on the same div like *ngIf and *ngFor you can use <ng-container> which doesn’t add an extra DOM element to your tree.

Optimise change detection

These changes require a more in-depth understanding of Angular and how change detection works.

Use OnPush changeDetection. setting changeDetection:ChangeDetectionStrategy.OnPush in your component essentially tells Angular to avoid deep checking input array/object and only run change detection for that component sub-tree if the inputs are not equal by reference. Which can save you both the time the framework takes for deep equality checks as well as the change detection time for that component and all other children of that component that would be marked as dirty as a result. Use trackBy. If you’re rendering an array of items with *ngFor that may change over time, you can tell Angular to bind each DOM element with a value from an item in that array like an item’s id. The next time the array changes, Angular will only render/update the changed items instead of re-rendering the entire array of DOM elements. Use ngZone.runOutsideAngular. If you use asynchronous APIs like setTimeout , setInterval and Promise in your component that does not change your component’s state in their callback and does not need to trigger change detection you can tell Angular not to trigger change detection when their callback is Executed.

ngZone.runOutsideAngular(() => {

setTimeout(() => {

doSomethingThatDoesntUpdateState() }, 5000) })

Or completely disable ngZone from your application and manually trigger change detection for asynchronous javascript APIs with applicationRef.tick() for a more advanced usecase

platformBrowserDynamic().bootstrapModule(AppModule, {

ngZone: 'noop';

})

Conclusion

There are other things you can do to optimise your application’s performance, most of them already are enabled by default now like Pure Pipes, enableProdMode, AOT & build-optimizer with ng build --prod .

This article was inspired by angular-performance-checklist written by Minko Gechev, If you’re interested about other performance optimisations you can do and more detailed explanations, you should check it out.