By Poojitha Srinivasan
Angular is a great framework which is well suited for developing large app built to get the highest performance on the web. But sometimes as a developer we end up doing things which result in poorly performing app. This blog is mainly about Angular specific best practices to have best load time and runtime performance.
Load Time Performance
- Ahead Of Time (AOT) Compilation: On the contrary to JIT (Just In Time) Compilation where the compilation is done in the browser, AOT also called offline compilation which compiles the code during the build process thus reducing much of the processing overhead on the client browser. With your angular-cli just specify the “aot” flag (if prod flag is present, then aot flag not required) and AOT will be enabled.
- Tree-shaking: This is the process of removing unused code thus resulting in smaller build size. On your angular-cli, Tree-Shaking is enabled by default.
- Uglify: In this process the code size is reduced using various code transformations like mangling, removal of white spaces, removal of comments etc. For angular-cli specify the “prod” flag to perform the uglification process.
- Prod flag: For production, build specify the “prod” flag in the angular-cli application. It will enable various build optimizations like, AOT, uglify, removal of source maps, service workers (if enabled) producing a much smaller build size.
- Build-optimizer flag: If you are using angular-cli make sure you specify “build-optimizer” flag for your production build. It will disable the vendor chunk and will result in more smaller code.
- Lazy loading: Lazy loading is the mechanism where instead of loading complete app, we load only the modules which are required at the moment thereby reducing the initial load time. In simple words, it doesn’t load something which you don’t need.
- Updating Angular and angular-cli: Updating your Angular and angular-cli regularly gives you the benefit of many performance optimizations, bug fixes, new features, security etc.
- RxJS 6: RxJS 6 makes the whole library more tree-shakable thereby reducing the final bundle size. RxJS is a library for reactive programming which uses Observables, to compose asynchronous or callback-based code.
- Updating Third Party Packages: Make sure you are regularly updating your third party packages. Many of newer packages may contain many performance improvements including smaller size and other build time performance optimizations (e.g. RxJS 6). Also by updating the packages regularly, you may get many improvements related to the bug fixes, security vulnerability fixes, fixes related to package compatibility etc.
- Compressing images: It’s a good idea to compress the images without losing much of the quality thereby saving the bytes transferred over the network improving the build time. There are many tools available to achieve this. Visual Studio Code extension called TinyPNG can be used to compress Jpeg and PNG images without losing much of the quality.
- Remove unused fonts: It’s a good idea to remove the unused fonts which may help you save few bytes over the network.
- Slow DNS and SSL: Sometimes your DNS and SSL provider could be the reason for slow load time. So make sure the DNS and SSL are fast and configured properly.
Run Time Performance
- Change Detection: By default on each asynchronous event, Angular does a dirty checking by performing a change detection for the whole component tree. Such dirty checking could be a lot computational heavy for a medium to large apps. You can drastically reduce this by setting “ChangeDetectionStrategy” to “OnPush”. Thus By setting the “onPush”change detection strategy we are signing a contract with Angular that obliges us to work with immutable objects.
- Detach Change Detector: We can completely detach the component from change detection thereby giving a developer the control to inform Angular as to when and where to perform the change detection.
- trackBy: Manipulating the DOM is an expensive task, and this can be very evident when it comes to rendering long lists of items, usually achieved by using the *ngFordirective. By default, *ngFor identifies object uniqueness by reference. If the object reference is broken while updating the content of the object, Angular removes the related DOM node completely and recreate it again even though the actual change required is for only a small part of the DOM node. This issue can be solved by using trackBy.
- Pure Pipes: In the “@Pipe” decorator you can specify “pure” flag as true. This flag indicates that the pipe is not dependent on any global state and is side effect free. It enables Angular to cache the outputs for all the input parameters the pipe has been invoked with and thus allows to reuse the values instead of recomputation. This can lead to massive reduction in the duplicate operations performed in many cases thus hugely improving the performance.
- Avoid complex computations in the template: Avoid doing complex calculation in the HTML template (ex. calling the component method inside the template), instead leverage the use of pure pipes which takes the advantage of Angular caching and hence avoiding duplicate operations. If the use of pipe is not possible, we can pre-calculate the values and then directly bind values instead of calling the component method in the template.
- Unsubscribing Observables: Observables can create memory leak issue. Hence it is better to unsubscribe them when they are not needed anymore. However, you don’t have to unsubscribe all observables used. Unsubscribing explicitly is only required when a subscription is created inside a component which is destroyed before the observable completes.
- Observable share() operator:If you have subscribed the observable at multiple locations/components, then each subscription will try to produce the data even though the data is duplicate. We can avoid the processing of duplicate data across subscriptions using the “share()” operator.