Friday, April 10, 2020

The Complete Guide to Angular Performance Tuning

It is common to see Angular apps gradually getting slower over time. Even through Angular is a performant framework, apps will start to become slow as they grow if you are not aware of how to develop performant Angular apps. It is, therefore, a must for any serious Angular developer to be aware of what will make an Angular app slow, so they can avoid it becoming slow in the first place.
In this post, I will go through different methods that can be used to performance tune Angular apps. Some of them involve improving the change detection, others involve the page load and UX in general.
The book Code Complete writes about performance tuning as a tradeoff between code quality and performance. If you can do something that will both improve code quality and performance it is considered best practice and it is a good idea to do that from the start. This post will deal with both the best practices you should almost always strive to do and performance tuning for a specific problem.

Improving change detection

Change detection can be the most performance heavy in Angular apps and therefore it is necessary to have some awareness of how to render the templates most effectively, so you are only rerendering a component if it has new changes to show.

OnPush change detection

The default change detection behavior for components is to re-render every time an asynchronous event has happened in the app such as click, XMLHttpRequest, setTimout. This can become a problem because this will cause many unnecessary renderings of the templates, that may not have been changed
OnPush change detection fixes this by only re-rendering a template if either:
  1. One of its input properties has gotten a new reference
  2. An event from the component or one of its children eg. click on a button in the component
  3. Explicit run of change detection
To apply this strategy you just need to set the change-detection strategy in the component’s decorator:
@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoListComponent implements OnInit {

Design for immutability

To leverage this method you need to make sure, that all state changes are happening immutably because we need a new reference provided to a component’s input to trigger change detection with onPush. If you are using Redux for state management, then you would naturally get a new instance every time the state changes, which will trigger change detection for onPush components when provided to a component’s inputs. With this approach you want to have container components, that is responsible for getting the data from the store and presentation component, which will only interact with other components using input and output.
Billedresultat for container and presentational components
The easiest way to provide the store data to the template is using the async pipe. This will look like having the data outside of an observable and will make sure to clean up the stream when the component gets destroyed automatically.
<div class="todo-list-wrapper" *ngIf="(todoList$ | async) as todoList">
<div class="mx-auto col-10">
<h5>{{'todo-list' | translate}}</h5>
<hr>
<app-cards-list [tableRef]="todoListRef" [cardRef]="todoItemCardRef" [data]="todoList"></app-cards-list>
</div>
<hr>
<app-add-todo [currentTODO]="currentTODO"></app-add-todo>
</div>

Make onPush the default change detection strategy

Using schematics you can make onPush the default changeDetection strategy when generating new components with Angular CLI. You simply add this to the schematics property in Angular.json:
"schematics": {
"@schematics/angular:component": {
"styleext": "scss",
"changeDetection": "OnPush"
}
},
view rawangular.json hosted with ❤ by GitHub

Using pipes instead of methods in templates

Methods in a template will get triggered every time a component gets rerendered. Even with onPush change detection, that will mean that it gets triggered every time there is interaction with the component or any children of the component (click, type). If the methods are doing heavy computations, this will make the app slow as it scales as it keeps recomputing every time there is interaction with the component.
What you can do instead is using a pure pipe to make sure, that you are only recomputing when the input to the pipe changes. async pipe, as we looked at before, is an example of a pure pipe. It will only recompute when the observable emits a value. We want to make sure, that we are only recomputing when the input changes if we are dealing with pure functions. A pure function is a function that always will return the same output given the same input. For that reason, it doesn’t make sense to recompute the output if the input has not changed.

With method

Let’s start looking at what happens if you use a template method instead of a pipe.
Consider we have the following method:
public getDuedateTodayCount(todoItems: TODOItem[]) {
console.log('Called getDuedateTodayCount');
return todoItems.filter((todo) => this.isToday(new Date(todo.dueDate))).length;
}
private isToday(someDate) {
const today = new Date();
return (
someDate.getDate() == today.getDate() &&
someDate.getMonth() == today.getMonth() &&
someDate.getFullYear() == today.getFullYear()
);
}
Being called in the template like this:
{{'todo-list-section.todos-duedate-today' | translate}}: {{getDuedateTodayCount(todoList)}}
This has the consequence of triggering the method every time a button is clicked inside of the component that is even using onPush change detection:

With pipe

We fix this by converting the method to a pipe, as a pipe as default is pure it will rerun the logic if the input changes (reference change).
By creating a new pipe and moving the logic we used before inside of the pipe we get:
import { Pipe, PipeTransform } from '@angular/core';
import { TODOItem } from '@app/shared/models/todo-item';
@Pipe({
name: 'duedateTodayCount'
})
export class DuedateTodayCountPipe implements PipeTransform {
transform(todoItems: TODOItem[], args?: any): any {
console.log('Called getDuedateTodayCount');
return todoItems.filter((todo) => this.isToday(new Date(todo.dueDate))).length;
}
private isToday(someDate) {
const today = new Date();
return (
someDate.getDate() == today.getDate() &&
someDate.getMonth() == today.getMonth() &&
someDate.getFullYear() == today.getFullYear()
);
}
}
Which is used like this in the template:
{{'todo-list-section.todos-duedate-today' | translate}}: {{todoList | duedateTodayCount}}
Now, this pipe is only being triggered when the input (todolist) has changed.

Cache values from pure pipes and functions

Even when using pure pipes, we can optimize this further by remembering/caching previous values, so we don’t need to recompute if we already run the pipe with the same input in the past. Pure pipes don’t remember the previous values, but will just make sure that if the input hasn’t changed the reference, it will not recompute. To do the caching of previous value we need to combine it with something else.
An easy way to do this is to use Lodash memorize method. In this case, this is not very practical as the input is an array of objects. If the pipe was taking a simple data type, such as number as input, it could be beneficial to use this as a key to cache results and thus avoid recomputation.

Using trackBy in ngFor

When using ngFor and updating the list, Angular will by default remove the whole list from the DOM and create it again, because it has no way, by default, to know which item has been added or removed from the list. The trackBy function is solving this by allowing you to provide Angular with a function used for evaluating, which item has been updated or removed from the ngFor list, and then only rerender that.
The track by function looks like this:
public trackByFn(index, item) {
return item.id;
}
This will track changes in the list based on the id property of the items (todo items).
The trackBy function is used in the template like this:
<ul class="list-group mb-3">
<app-todo-item-list-row *ngFor="let todo of todos; trackBy: trackByFn" [todoItem]="todo" (todoDelete)="deleteTodo($event)" (todoEdit)="editTodo($event)"></app-todo-item-list-row>
</ul>
For a list where you can interact with it (add, delete), then it is a good idea to use trackBy. For static lists, which are not being changed, this will not make a difference to the user experience.

For heavy computations: Detach change detection

In extreme cases, you would want to only trigger change detection manually for some components. That is if a component is instantiated 100’s of times on the same page and rerendering every one of them is expensive you can turn off automatic change detection completely for the component and only trigger changes manually in the places it is necessary.
If we wanted to do this for the todo items we could detach change detection and only run this when the todo Item is set in the todoItem set property:
@Component({
selector: 'app-todo-item-list-row',
templateUrl: './todo-item-list-row.component.html',
styleUrls: ['./todo-item-list-row.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoItemListRowComponent implements OnInit {
private _todoItem : TODOItem;
public get todoItem() : TODOItem {
return this._todoItem;
}
@Input()
public set todoItem(v : TODOItem) {
this._todoItem = v;
this.cdr.detectChanges();
}
@Input() public readOnlyTODO: boolean;
@Output() public todoDelete = new EventEmitter();
@Output() public todoEdit = new EventEmitter();
@Output() public todoComplete = new EventEmitter<TODOItem>();
constructor(private cdr: ChangeDetectorRef) {}
public ngOnInit() {
this.cdr.detach();
}
public completeClick() {
const newTodo = {
...this.todoItem,
completed: !this.todoItem.completed
};
this.todoComplete.emit(newTodo);
}
public deleteClick() {
this.todoDelete.emit(this.todoItem.id);
}
public editClick() {
this.todoEdit.emit(this.todoItem);
}
}

Improving page load

The page load time is an important aspect of user experience today. Every millisecond a user is waiting, potentially means a loss in revenue, because of a higher bounce rate and worse user experience, so this is a place you should optimize. Page load time also has an impact on SEO, as faster websites are rewarded by search engines.
For improving page load we want to use caching using Angular PWA, lazy loading and bundling.

Cache static content using Angular PWA

Caching the static content will make your Angular app load faster as it will already be in the browser. This is easily done using Angular PWA which will use service workers to cache the static content, that is the js, css bundles, images and static served files, and present them without making a call to the server.
I have already created a guide to how to setup caching with Angular PWA you can read here.

Cache HTTP calls using Angular PWA

With Angular PWA you can easily set up caching rules for HTTP calls to give a faster user experience without cluttering your app with a lot of caching code. Either you can optimize for freshness or performance, that is, you can either choose to only read the cache if the HTTP call times out or first check the cache and then only call the API then the cache expires.
I have a guide with a video showing you how to do this here.

Lazy load routes

Lazy loading routes will make sure that a feature will be bundled in its own bundle and that this bundle can be loaded when it is needed.
To set up lazy loading we simply create a child route file like this in a feature:
const routes: Routes = [
{
path: '',
component: TodoListCompletedComponent
}
];
export const TodoListCompletedRoutes = RouterModule.forChild(routes);
Then add this route to imports:
@NgModule({
imports: [FormsModule, CommonModule, SharedModule, TodoListCompletedRoutes],
declarations: [TodoListCompletedComponent]
})
export class TodoListCompletedModule {}
And finally lazy load it using loadChildren in the root route:
const appRoutes: Routes = [
{
path: rootPath,
component: TodoListComponent,
pathMatch: 'full'
},
{
path: completedTodoPath,
loadChildren: './todo-list-completed/todo-list-completed.module#TodoListCompletedModule'
}
];
export const appRouterModule = RouterModule.forRoot(appRoutes);
view rawapp.routes.ts hosted with ❤ by GitHub

Optimizing bundling and preloading

To optimize page load even further you can choose to preload the feature modules, so navigation is instant when you want to render a lazily loaded feature module.
This can be done by setting the: preloadingStrategy to PreloadModules as:
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})
On load, all the feature modules will be loaded, giving you both a faster page load as well as instant navigation when you want to load other feature modules. This can even be optimized further by creating your own custom preloadingStrategy like shown here to load only a subset of the routes on app initialization.

Server-side rendering with Angular Universal

For Angular apps that are containing indexed pages, it is recommended to server-side render the app. This will make sure the pages are being fully rendered by the server before shown to the browser which will give a faster page load. This will require that the app is not dependent on any native DOM elements, and you should instead inject eg. document from the Angular providers.
Read more about how to setup Angular Universal in an app here.

Improving UX

Performance tuning is all about optimizations at the bottleneck, that is the part of the system that is affecting your user experience the most. Sometimes the solution could just be to handle actions more optimistically and thus less waiting for the user.

Optimistic updates

Optimistic updates are where an update is reflected in the UI before it is saved on the server. This gives a more snappy native-like experience to the user. The consequence with this is that you need to roll the state back in case the server fails to save the changes. Strongbrew has written a post about how you can implement this in a generic way, making optimistic updates easy to incorporate in your app. You can read it here.

How should I prioritize performance tuning?

Start with low hanging fruits: onPush, Lazy loading and then PWA and then gain awareness of where your performance bottlenecks are in the system. Every improvement that is not at the bottleneck is an illusion as it will not improve the user experience with the app. Tuning methods like detaching the change detection should only be used if you have a specific problem with a component’s change detection impacting performance.

The Angular performance tuning steps

This is the steps you will go through as you performance tune your Angular application. You should only move up the steps until the performance problems are fixed, no reason to over-engineer improvements that will not improve the UX.
  1. OnPush
  2. Lazy loading modules
  3. Improve page load with Angular PWA
  4. trackBy for ngFor
  5. Pure pipes instead of methods (including async)
  6. Cache values from pipes and pure functions
  7. Cache HTTP requests better
  8. Detach/manual change detection
  9. Angular Universal
Why is Angular Universal the last one? Because introducing server-side rendering can cause big changes to the development setup (need to maintain another server, cannot reference DOM and need to maintain a server and a client bundle) and should be used either for performance reasons that can not be fixed with the previous steps or SEO purposes.
A complete demo project can be found here.

Conclusion

In this post, we saw how to performance tune your Angular app. We dived into a couple of different performance tuning categories: change detection, page load, and UX improvements. The way you should go about any improvement in a system is that you should first identify bottlenecks and try to solve them first using one of the methods from this post. Anything else might just be a waste of time if it is not improving the user experience.

44 quick tips to fine-tune Angular performance

In this post, I have put together Angular specific best practices to have best load time and runtime performance. Also, apart from the Angular, I have also mentioned some general best practices in web development. My intention in this post is just to list down the best practices and avoiding the detailed explanation thus keeping the post small.

Load Time Performance

1. AOT: As opposed to JIT Compilation where the compilation is done in the browser, AOT compiles much of the code during the build process (also called offline compilation) thus reducing much of the processing overhead on the client browser. With angular-cli just specify the “aot” flag (if prod flag is present, then aot flag not required) and AOT will be enabled.
2. Tree-shaking: This is the process of removing unused code resulting in smaller build size. If you are using angular-cli, Tree-Shaking is enabled by default.
3. Uglify: It is the process where the code size is reduced using various code transformations like mangling, removal of white spaces, removal of comments etc. For webpack use uglify plugin and with angular-cli specify the “prod” flag to perform the uglification process.
4. Google Closure Compiler: This is the compiler used by Google for their products which results in much smaller bundle size compared to Webpack uglify by performing much more aggressive minification. Even though it is not officially supported by the Angular team, you can look this implementation with closure compiler.
5. Webpack 4: Using Webpack 4 (and higher) for your angular-cli or custom webpack build results in more smaller build size compared to Webpack 3. Webpack 4 has mode option which lets you specify the optimization type (production or development) without you requiring to write any explicit configuration giving you the best possible results for the target environment. Also, build time with Webpack 4 is much faster (60% to 98%) than the earlier version thereby reducing the development time.
6. Prod flag: For the production, build specify the “prod” flag in the angular-cli application. It will enable various build optimizations like uglify, AOT, removal of sourcemaps, service workers (if enabled) producing a much smaller build size.
7. Build-optimizer flag: If you are using angular-cli make sure you specify “build-optimizer” flag for your production build. This will disable the vendor chunk and will result in more smaller code.
8. 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 means “don’t load something which you don’t need.
9. Server side rendering: Rendering the first page of your application on the server (using Node.js, .Net, PHP) and serving it as a static page causes near to instant rendering thus greatly improves perceived performance, speed, and overall user experience. You can use Angular Universal to perform server side rendering.
10. Progressive Web App: PWA makes your app load much faster, it gives the offline capability to your app and gives near native app experience thus greatly improving overall perceived performance by the user.
11. Ivy Render Engine: Angular team recently announced a new render engine named Ivy. It results in much smaller bundle size than the current engine with improved debugging experience. Though it is still not production ready you can still try it in your app. You can look at this ng-conf keynote for more details.
12. 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.
13. RxJS 6: RxJS 6 makes the whole library more tree-shakable thereby reducing the final bundle size. However, it has some breaking changes like operators chaining is not possible instead, pipe() function (helps in better tree shaking) is introduced to add operators. They have also renamed some operators.
14. Service worker cache: If you have configured your app to support Progressive Web App, make sure to specify all the necessary static resources in the PWA config JSON. These static files will be cached in the client’s browser making the second time load much faster.
15. Cache-control header: cache-control header controls who caches the response under what condition and for how long thus eliminating the need for network round trip for the resources which are cached.
16. Third party packages: Review the third party packages you are using and see if better and smaller alternative is available as it may reduce the final size of your build.
17. Unnecessary use of third-party packages: If you include a third-party package just to achieve a small functionality which could be easily done natively with JavaScript or Angular then you are adding unnecessary size overhead to your app which could have been easily saved. For example, if you are including Lodash just to do a simple object filtering then it is totally unnecessary as you could do the same thing natively in JavaScript.
18. defer attribute: Mentioning defer attribute to your script tag will defer the loading of the scripts (sychronous) until the document is not parsed thus making your site interactive quicker. For angular-cli app currently there is no way to add this automatically during the build, you have to do it manually after the build.
19. async attribute: Just like the defer attribute, async delays the loading of scripts until the document is not parsed but without respecting the order of loading of the scripts. The best example to use it with google analytics script which usually independent of any other scripts.
20. Gzip compression: Gzip compression can greatly decrease the size of the response body and hence increase the speed of a web app. Make sure you have enabled gzip compression in your backend. For express.js you can add compression middleware.
22. Preload and Prefetch attributes: These attributes help to load the static resources as quickly as possible thus improving the time for first meaningful paint. Having faster first meaningful paint greatly affect the user experience. Preload and Prefetch are almost similar with the only difference is that Preload resources have greater priority. So use Preload for the assets which are essential for the initial rendering and use Prefetch for the resources which are required after site load (required in future across the pages). You can read more about these attributes here.
23. Updating Third Party Packages: Make sure you are regularly updating your third party packages. Many times newer packages may contain many performance improvements including smaller size and other build time performance optimizations (e.g. RxJS 6). Also by regularly updating the packages, you may get many improvements related to the bug fixes, security vulnerability fixes, fixes related to package compatibility etc.
24. 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. VS Code extension called TinyPNG can be used to compress Jpeg and PNG images without losing much of the quality.
25. Remove unused fonts: It’s a good idea to remove the unused fonts which may help you save few bytes over the network.
26. 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 correctly.

Run Time Performance

27. Change Detection: By default on each asynchronous event, Angular performs a dirty checking by performing a change detection for the whole component tree. Such dirty checking could be a lot computation heavy for a medium to large apps. You can drastically reduce the change detection by setting “ChangeDetectionStrategy” to “OnPush”. The OnPush strategy promotes the use of immutable data structures.
28. 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.
29. Web Workers: The JavaScript implementation in all browser is single threaded thus making the whole app to run on a single thread. Such single-threaded execution drastically reduces the frame rate of the complex application as both UI painting and JS execution handled by the same thread. As Angular by default avoids direct DOM manipulation, it is possible to run the entire Angular app in a separate web worker thread thereby keeping the main thread free to just handle the UI rendering. Check this post to see how to run an angular-cli app inside web worker. However, there are many npm packages which try to access DOM directly thereby creating issues while running the whole app inside the worker process. If you want to run only a piece of code under a web worker thread then look at this npm package.
30. Webassembly: Webassembly is a low level assembly like a language enabling near-native performance. WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms. You can take advantage of Webassembly to run some amount of your web app code with Webassembly. Take a look at this demo app created with angular-cli and wasm. However, you should be aware of the fact wasm is still new and using it sometimes may be tricky considering as of now it has only 4 supported data types (2 floating points and 2 integer points). Also as of now, for most of the cases, performance benefit of wasm is not that huge when you are using it to execute a small piece of code compared to inline JS. Properly evaluate which code you want to move to wasm.
31. trackBy: By default, *ngFor identifies object uniqueness by reference. If the object reference is broken by 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 easily solved by using trackBy.
32. Pure Pipes: In the “@Pipe” decorator you can specify “pure” flag as true. This flag indicates that the pipe is not dependent on any outside or global state and is side effect free. This 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 a massive reduction in the duplicate operations performed in many cases thus hugely improving the performance.
33. Avoid complex computations in the template: Avoid doing complex calculation in the HTML template (ex calling some component method inside the template), instead leverage the use of pure pipes thereby taking advantage of Angular caching and hence avoiding duplicate operations or if the use of pipe is not possible, see the opportunity to pre-calculate the values and then directly bind values instead of calling the component method in the template.
34. enableProdMode: Invoking “enableProdMode()” avoids Angular from performing additional checks for change detection.
35. AOT Compilation: AOT not only improves the build time performance but also the runtime performance of the app.
36. Optimize Events: Slower DOM events block change detection until the event is not completed. For example, if you have a click event in your template which is handled in the component and the component itself is calling service method to process it, the change detection will not complete until the control is not returned from the service. If your service is taking more time to perform the intended operation, then it will ultimately slow down the change detection. See the opportunity to optimize your logic to improve the duration or if possible try to move your service logic to separate web worker thread or use wasm if needed.
37. Unsubscribing Observables: Observables could create the memory leak issue. So it is better to unsubscribe them when they are not needed anymore. However, you don’t have to unsubscribe all the observables used. Unsubscribing explicitly is required when a subscription is created inside a component which is destroyed before the observable completes. Check this SO thread for more info.
38. 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 the duplicate data across subscriptions using the “share()” operator.
39. Progressive Web Apps: The PWA not just give you a load time optimization but also the runtime optimizations making your app more responsive, interactive, fast, smooth animations, offline support etc.
40. Updating Third Party Packages: Again regularly updating your third party packages may also result in better run time performance.
41. console.log(): Using console.log() statements in your production code could be a bad idea as it will slow down the performance of the app and also logging objects with console.log() creates memory leak issue. When browser’s console window is open, the console.log() execution slows down even further by many times thus impacting site’s performance significantly. It’s better to completely remove the console.log() statements from your production code or at least have an environment specific conditional logging.
42. Global Variables: There are many disadvantages of using global variables and one of them is the memory leak. The variables defined in the global scope won’t be cleared until the window is reloaded or tab is closed thus resulting in the memory leak if the global variable is not intended to be used throughout the app. If for some reason you want to have global variables, there are better ways to do it in the Angular.
43. Event listeners: Adding event listeners to your DOM node could create memory leak issue. If you forget to remove the listener inside the $destroy event of your directive, it will hold a reference to a DOM node even if it is removed from the document. The DOM tree will then become a “Detached DOM tree” and will leak. Modern JS engines are able to figure most of this situations for you and remove the listeners, but more complex tree hierarchies can challenge even the best GC.
44. Bad Third Party Packages: If a bad third party package having performance issues (memory leak, costly js execution, security etc) will ultimately affect your app’s performance. So it is always advised to properly review any third party package before using.
So these are all the tips you should follow to write a high-performance Angular app. Hopefully, this will help you to fine-tune your Angular app. Also make sure you do proper performance analysis and audit of your app using different tools available like Chrome/Edge/Firefox JavaScript Profiling tool, Heap snapshots comparison, Chrome Lighthouse etc to make a proper judgment of what exactly causing the issue.

How to get the best performance out of your Angular apps

Angular is a great framework and can be used for developing large scale applications, but can be tricky to fine tune and achieve good load time and run-time performance. In this post, I’ll detail some best practices I have learned along the way, so you will not make the same mistakes I made.

Change detection

Change detection is Angular’s mechanism to see if there are any values that have been changed and require the view to be updated. By default, Angular runs change detection with nearly every user interaction. In order to know if the view should be rendered again, Angular accesses the new updated value, compares it with the old one and makes the decision. As your application grows, it will include a lot of expressions, and having a change detection cycle on each one of them will cause a performance problem.
We can optimize things if we create a ‘dumb’ component with a certain attribute to handle the change detection cycles. This component relies only on non specific input data and in that way, we can tell Angular only to run change detection when an input changes or when we manually trigger it. When a reference type is immutable, every time the object is being updated, the reference on the stack memory will have to change. Now we can have a simple reference check on the object between the memory address and the stack. If the memory address has changed, then we check all the values. This will skip change detection in that component.
We need to keep in mind that primitive types such as numbers, booleans, strings, etc are passed by value. Objects, arrays and functions are also passed by value, but the value is a copy of a reference.
You can look for more details on that here.
And now we will see two examples of how this is implemented.

Example: ChangeDetectionStrategy.Default

You don’t have to specify changeDetection type, it will be ‘ChangeDetectionStrategy.Default’ by default.
Default change detection
Photo by Tim Mossholder on Unsplash

ChangeDetectionStrategy.OnPush

In order to use the OnPush change detection, we need to modify the child component from the first example.
OnPush change detection

Minimize DOM manipulations

If you have some list of data that was retrieved from some server and you need to show it, you are probably using the Angular directive, ngFor. Angular will create a new template for you for each item in that list.
If at some point some of the data has been changed, Angular can’t really know that and will replace the whole list, instead of just the items that were changed. In order to improve that, Angular provide us with the trackBy function.trackBy takes a function which has two arguments: index and item. If trackBy is given, Angular tracks changes by the return value of the function.
Syntax:
Most common use is just to return the index itself or item.id as a unique identifier for the item: trackByFn(index, item){ return item.id; }.
With that, Angular can track which items have been added or removed according to the unique identifier and create or destroy only the things that were changed.

Avoid using methods in your template

While it is very convenient to use methods in Angular templates, Angular will recalculate them on each change detection. For larger lists it will affect rendering time and the application may even get stuck due to huge memory consumption. In the following example, Angular will run getNumberOfCars on each change detection cycle (ie upon adding a new row).
How can we handle this situation? We can pre-compute the results and then just access the data we have computed. In our example, we can add a new attribute to the person object, vehiclesNumber, which holds in advance the amount of vehicles each person has. The other way to do this is by implementing the method getNumberOfCars as a pure pipe.
Angular executes a pure pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (StringNumberBooleanSymbol) or a changed object reference (DateArrayFunctionObject).
Angular ignores changes within (composite) objects.
This may seem restrictive but it’s also fast. An object reference check is fast — much faster than a deep check for differences — so Angular can quickly determine if it can skip both the pipe execution and a view update.
The pipe will still be executed on each change detection cycle. However, if a pipe is executed more than once with the same parameters, the results of the first execution are returned. Meaning, Angular will cache the results for better performance.
Let’s see an example.
Without a pipe:
While with a pipe we will get:
We can see it will recalculate only on the new data, instead of the whole list.

Use Prod flag in production

It will disable Angular’s development mode, which turns off assertions and other checks within the framework. This will also increase your performance. You can find more details here.

Don’t use console.log in production code

console.log prints can really slow down your application, as it takes some time to compute what you want to print. Also, for long information it will also consume some more time for the printing process.

Don’t forget to unsubscribe from your observables

Your subscription holds a reference to your component instance. If you will not unsubscribe from it, the instance will not be cleared by the garbage collector which will cause a memory leak. You can unsubscribe easily by using ngOnDestory(){this.subscription.unsubscribe();}. You can read more about it here.

Final Words

If you run into any issues, feel free to drop me a line at : markgrichanik[at]gmail[dot]com.
I would also love to hear any feedback/tips you have while working on large scale applications with Angular.