Angular ~ Optimization & Performance - rohit120582sharma/Documentation GitHub Wiki

Table of contents

  • Optimization
    • Minification
    • Uglification
    • Bundling
    • Dead code elimination
    • Ahead-of-time (AOT) Compilation
  • Application Performance
    • Change Detection
    • Use trackBy option for *ngFor directive
    • Remove subscription in ngOnDestroy life-cycle hook
    • Using Lazy Loading
  • Other techniques
    • Angular Universal
    • Web Workers
    • Service Workers
  • References

Compilation

Every Angular application involves a compilation step to compile the template code into JavaScript code.

When our application starts, Angular compiler is going to kick in and to walk down the tree of components. It is going to parse the template of each component and produce the JavaScript code to create the same structure in the DOM.

Angular offers two types of compiling the code.

JIT

It is default compilation.

We develope our code. Next, we ship the code in production which can be served in the browser. Next, we view it in a browser, so the app (whole source code) gets downloaed. Next, Angular bootstraps the application. In this step, it parses and compiles all templates (to JavaScript).

AOT

We develop our code. Next, we allow angular to parse and compile all templates (to JavaScript) ahead of time. In other words, allow angular to understand our templates at an earlier point of time. Next, we ship the code in production. Next, we view it in a browser, so the app (pre-compiled code) gets downloaded.

Faster Startup since Parsing and Compilation doen't happen in browser.

Templates get checked during development.

Smaller File Size as unused features can be stripped out and the compiler itself isn't shipped.

More secured, because JIT uses eval.

AOT with Angular-CLI

Need to run ng build command with some flags. The --prod flag tries to optimize and minify the code. The --aot flag uses AOT compilation. This command creates dist folder as production build.

ng build --prod --aot

Tree Shaking

Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e. import and export.

For tree shaking use ng build --prod --build-optimizer. This way vendor.js and main.js are combined in main.js file. To avoid that you have to add --vendor-chunk=true.


Change Detection

Zones

Zone.js provides a mechanism, called zones, for encapsulating and intercepting asynchronous activities in the browser (e.g. setTimeout, , promises). Zone.js monkey patches all asynchronous APIs in the browser and triggers the change detection in the end of the execution of any async callback.

These zones are execution contexts that allow Angular to track the start and completion of asynchronous activities and perform tasks as required (e.g. change detection). Zone.js provides a global zone that can be forked and extended to further encapsulate/isolate asynchronous behavior, which Angular does so in its NgZone service, by creating a fork and extending it with its own behaviors.

The NgZone service provides us with a number of Observables and methods for determining the state of Angular's zone and to execute code in different ways inside and outside Angular's zone.

References

Change Detection

When you change any of your models, Angular detects the changes and immediately updates the views. This is change detection in Angular. The purpose of this mechanism is to make sure the underlying views are always in sync with their corresponding models.

A model in Angular can change as a result of any of the following scenarios:

  • DOM events (click, hover over, etc.)
  • AJAX requests
  • Timers (setTimer(), setInterval())

Change Detector

All Angular apps are made up of a hierarchical tree of components. At runtime, Angular creates a separate change detector class for every component in the tree, which then eventually forms a hierarchy of change detectors similar to the hierarchy tree of components.

Whenever change detection is triggered, Angular walks down over the entire tree of change detectors to determine if any of them have reported changes. The change detectors provide a way to keep track of the component’s previous and current states as well as its structure in order to report changes to Angular. If Angular gets the report from a change detector, it instructs the corresponding component to re-render and update the DOM accordingly.

The change detection cycle is always performed once for every detected change and starts from the root change detector and goes all the way down in a sequential fashion. This sequential design choice is nice because it updates the model in a predictable way since we know component data can only come from its parent.

A way to improve the performance of the change detection is to not perform it for subtrees which are not supposed to be changed based on the recent actions.

ChangeDetectionStrategy

The Default change detection strategy check for any differences between the previous state and current state of the overall application model. The question that Angular asks in the default change detection strategy is: Has any value in the model changed?

The OnPush change detection strategy allows us to disable the change detection mechanism for subtrees of the component tree. By setting the change detection strategy to any component to the value ChangeDetectionStrategy.OnPush, will make the change detection perform only when the component have received different inputs. Angular will consider inputs as different when it compares them with the previous inputs by reference, and the result of the reference check is false. In combination with immutable data structures OnPush can bring great performance implications for such "pure" components.

import {ChangeDetectionStrategy, Component} from '@angular/core';

@Component({
	// ...
	changeDetection: ChangeDetectionStrategy.OnPush
})
export class OnPushComponent {
	// ...
}

Detaching the Change Detector

Another way of implementing a custom change detection mechanism is by detaching and reattaching the change detector (CD) for given component. Once we detach the CD Angular will not perform check for the entire component subtree.

@Component({
	selector: 'giant-list',
	template: `
	<li *ngFor="let d of dataProvider.data">Data {{d}}</lig>
	`,
})
class GiantList {
	constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
		ref.detach();
		setInterval(() => {
			this.ref.detectChanges();
		}, 5000);
	}
}

Run outside Angular

In rare cases we may want given code to be executed outside the context of the Angular Zone and thus, without running change detection mechanism. In such cases we can use the method runOutsideAngular of the NgZone instance.

@Component({
	selector: 'giant-list',
	template: `
	<li *ngFor="let d of dataProvider.data">Data {{d}}</lig>
	`,
})
class GiantList {
	constructor(private ref: ChangeDetectorRef, private _zone: NgZone) {}
	ngOnChanges(changes: any) {
		this._ngZone.runOutsideAngular(() => {
			// change something
		});
	}
}

Use trackBy option for *ngFor directive

The *ngFor directive is used for rendering a collection. By default *ngFor identifies object uniqueness by reference.

If, at some point, we need to change the data in the collection, we will be faced with a problem, because Angular can’t keep track of items in the collection and has no knowledge of which items have been removed or added. As a result, Angular needs to remove all the DOM elements associated with the data and create them again. This can mean a lot of DOM manipulations, especially in the case of a big collection. And, as we know, DOM manipulations are expensive.

Developer can provide a hint for angular how to identify object uniqueness: custom tracking function as the trackBy option for the *ngFor directive. Tracking function takes two arguments: index and item. Angular uses the value returned from tracking function to track items identity.

@Component({
	selector: 'my-app',
	template: `
	<li *ngFor="let item of list; trackBy:identify">{{item.name}}</li>
`
})
export class App {
	list:[];

	identify(index, item){
		return item.name; 
	}

Using Lazy Loading

By default, Webpack will output all your app’s code into one large bundle. Lazy loading gives you the ability to optimize your application load time by splitting the application to feature modules and loading them on-demand.

⚠️ **GitHub.com Fallback** ⚠️