avatarNeo Liu

Summary

The web content provides nine advanced tips for optimizing Angular performance, focusing on JavaScript enhancements and best practices in web development.

Abstract

The article outlines nine key strategies for enhancing the performance of Angular applications from a JavaScript perspective. It emphasizes the importance of lazy loading modules, implementing the OnPush change detection strategy, managing subscriptions with unsubscribe or takeUntil, utilizing the AsyncPipe for automatic subscription handling, avoiding excessive use of @HostListener, replacing function calls in templates with pipes, employing virtual scroll for large lists, preferring the providedIn property for dependency injection, and using ES6 import statements for tree shaking. The author, with several years of Angular development experience, encourages readers to engage with the content and share additional performance optimization insights.

Opinions

  • The author advocates for lazy loading as a critical technique to reduce initial loading times in Angular applications.
  • They suggest that the OnPush change detection strategy can significantly improve performance by minimizing unnecessary re-rendering.
  • The article promotes the use of RxJs's takeUntil operator or manual unsubscription to prevent memory leaks from observables.
  • It recommends leveraging Angular's AsyncPipe for simplified subscription management and to prevent memory leaks.
  • The author advises against overusing @HostListener, proposing a temporary div with @HostListener as a more efficient alternative.
  • The preference for pipes over function calls within templates is highlighted to avoid unnecessary function executions during change detection cycles.
  • Virtual scrolling is recommended for handling large datasets efficiently, with Angular Material's cdk-virtual-scroll-viewport as a practical solution.
  • The author expresses a clear preference for using providedIn with @Injectable over traditional providers for better tree shaking and potential performance gains.
  • The article emphasizes the use of import statements over require() for compatibility with tree shaking, which is a modern JavaScript optimization technique.
  • The author acknowledges that performance optimization is a vast topic and invites readers to explore other aspects beyond JavaScript, such as CSS, backend, and DevOps optimizations.
  • They also provide a list of related articles for further reading on Angular optimization and other web development topics.
  • The author endorses an AI service as a cost-effective alternative to ChatGPT Plus (GPT-4), suggesting it offers similar performance and features.

9 Ultimate Tips to Optimise Angular Performance

Photo by Pixabay from Pexel

During a few years development experience with Angular, I have concluded a few fundamental tips/best practices to improve Angular app (Or web development) performance. Some of the tips are Angular-only, while some of them are universal for web development.

In this article, I will list 9 tips to improve your Angular app performance in JavaScript perspective.

If you feel something is important but missed in this article, it’s always welcomed to leave a comment and share with us.👏👏👏

Javascript Improvements

1.1 Lazy loading

For commercial app, your Angular probably consists of a few modules, each one is a big chunk of loading delay. To solve this, you can use lazy loading as suggested by the offical documentation.

const routes: Routes = [
  {
    path: 'lazy',
    loadChildren: () => import('./my.module').then(m => m.MyModule)
  }
];

1.2 ChangeDetectionStrategy.OnPush

When you create an Angular component, you can specify the changeDetection strategy to Default or OnPush. The former one is default and will be triggered every time when a change detection happened. OnPush, instead, will only run when it detected a change, e.g. @Input value change or a function() inside the component, which saves a lot of unnecessary re-rendering.

@Component({
  selector: 'app-comp',
  templateUrl: './app-comp.component.html',
  styleUrls: ['./app-comp.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})

1.3 Unsubscribe/takeUntil

When you have an observable inside your component which can keep emitting values and it complete on it self, you will need to unsubscribe it on component destroy to avoid memory leaks. Or, you can use takeUntil from RxJs.

dataHandler() {
  this.subscription = data$.subscribe();
  // takeUntil solution 
  // data$.pipe(takeUntil(this.destroy$)).subscribe();
}
ngOnDestroy() {
  this.subscription.unsubscribe();
  // takeUntil solution 
  // this.destroy$.next();
}

1.4 AsyncPipe

Followed with the last point, another way to handle the subscription and un-subscription is to let Angular do it for you. The async pipe subscribes to an Observable or Promise and unsubscribes automatically on component destroy to avoid potential memory leaks.

<ng-container *ngFor="let item of items$ | async">
  // Handler
</ng-container>

1.5 Avoid @HostListener

When you add a @HostListener to a component, it will work until the component is destroyed. So, if you only need few params from @HostListener during a short period of the host component’s lifecycle, then avoid add the @HostListener on the long-live component unless you have to. Instead, add the @HostListener to a temporary div and destroy it after you get what you want.

<div *ngIf="!hostListenerDataLoaded" hostListenerDirective (onValueChange)="yourDataHandler($event)">
</div>

1.6 Use Pipe instead of function call inside template

When you use a function call with Angular structural directives inside your component, Angular won’t know what happen and therefore will call the function every time when the change detection runs even if the returned result will remain the exactly same (stackblitz). If you have to handle the data inside the template, consider using pipe.

Data processed by function call in template:

<!-- Bad -->
<div *ngIf="yourHandler(data)"></div>

Data processed by pipe in template:

<!-- Good -->
<div *ngIf="data | yourHandlerPipe"></div>

1.7 Virtual Scroll

Rendering a large lists of data items for browser is a big job and will probably also affect the whole page response time. And moreover, those extra rendered data items will probably end up not being looked at all. Instead rendering all items, you should consider using virtual scroll for your items display. Angular Material has provided rt> for you.

<cdk-virtual-scroll-viewport itemSize="50">
  <div *cdkVirtualFor="let item of items">{{item}}</div>
</cdk-virtual-scroll-viewport>

1.8 Use ProvidedIn instead of Providers

When you want to have a dependency injection of a service, you can either declare the service with @Injectable() or use providers:[] at module or component level. This @Injectable()method is preferred because it enables tree-shaking of the service if nothing injects it. When you can use either providers or providedIn, always consider providedIn as priority.

// Bad
providers: [YourService],
// Good
@Injectable({ providedIn: 'root' }) // or a SpecificModule
export class YourService { }

1.9 Use import instead of require()

When you have a 3rd-party lib you will have to import into your util, it is always better to use import over require() if possible because import, the ES6 syntax, supports tree shaking when you compiling your code into minified javascript. More explanations can be found here.

Before ES6 modules, we had CommonJS modules which used the require() syntax. These modules were "dynamic", meaning that we could import new modules based on conditions in our code.

This dynamic nature of the CommonJS modules meant that tree shaking couldn’t be applied because it would be impossible to determine which modules will be needed before the code is actually run.

Wrapping Up

Performance optimisation is a big topic. Besides what we listed above related to JavaScript. There are many other optimisations in different perspectives, e.g. CSS, Backend or DevOps.

Other posts:

Angular
Web Development
Software Development
JavaScript
Technology
Recommended from ReadMedium