Thursday, August 18, 2016

Tips for Angular Js Perfomance

Introduction
AngularJS is powerful framework to create large scale web application. 
Provides lot of special features to develop the single page performance driven 
web application.
  1. ·        Data Binding
  2. ·        Directive
  3. ·        Services
  4. ·        Routing
  5. ·        Controllers
  6. ·        Dependency Injection

Angular JS is beyond above mentioned concepts.
 Being MVVM framework applications maintained in single source of model to synch 
data between various layers. We can build reusable components with declarative approach with directive in place.
Any of the modern MVC frameworks fails on the performance concerns.
 It obviously depends on understanding core process how such framework works and how to get best out of it.
This newsletter summarizes top critical points to be considered while developing angular application to gain the performance.

Performance Angular
Angular is sooooooo SLOW!  Well many of us over time experienced or heard this phrase from people saying. AngularJS is one of the modular frameworks with all performance enhancements built in, but they can’t solve all our problems.
No matter how fast the framework, we can all create sluggish code through bad practices and not understanding key concepts that help it perform well.

Performance of angular is highly depends on digest cycle and scope hierarchy.
 Angular is not basically slow. You can make it slow relatively easy by coding the way it should not be.
I have listed here top performance points which I had come across developing angular application over the years.



1.   Restrict ng-repeat Usage

One of the amazing directives from Angular JS is ng-repeat. We cannot even think about an app based on angular without its presence as a beginner.
Ng-repeat quietly affect performance when not used properly. Having nested ng-repeats is a quick and easy way to increase
your watcher count exponentially and should be avoided if at all possible.



tick

cross

1.      Use  ng-repeat  with "track by" expression  to boost performance

<li ng-repeat="item in array track by item.id></li>


1.      Refrain binding function to ng-repeat. Binded function called every time of digest cycle.

<tr ng-repeat="user in getUsers()">…</tr>

2.      Applying filter in ng-repeat is expensive and called for each digest cycle. Instead filter in controller and bind it.

<tr ng-repeat="product in products | filter: { color: 'red' }
">…</tr>



2. Prefer ng-if/ ng-switch over ng-show

Use ng-if in place of ng-show wherever possible. ng-show merely hides the element with display none and bounded
element would still hang in DOM.

ng-if  directive add or remove the elements based on demand which avoids the unnecessary DOM rendering.
Therefore the additional DOM elements and data-bindings are evaluated on demand.



tick

cross

1.      Here form would not render and model inside evaluated on demand

<button ng-click="item.showForm = !item.showForm”>Show</button>

<form ng-if=”item.showForm”>
  <input type=”text” ng-model=”item.userName” name=”username” />
</form>


1.      userName model value would be still evaluated and add the burden to watchers.

<button ng-click=" item.showForm = !item.showForm”>Show</button>

<form ng-show=”item.showForm”>
  <input type=”text” ng-model=”item.userName” name=”username” />
</form>


3. Bind Once When Possible

One of the reasons being Angular App slowness is more bindings in the app.  When watchers set in application angular
internal engine hold the responsibility to keep track of changes and update wherever required.

Applying changes to the bounded model set by watchers is called digest cycle. The more watchers present will take longer
digest cycle result in slow and unresponsive application.

Angular becomes slower with around 2,000 bindings due to the process behind dirty-checking. Lesser binding will boost up performance!



tick

cross

1.      Use one way binding moreover possible. The below fragment will be updated on page load and watcher would be removed after the first digest cycle.


<h1> {{ ::appTitle }} </h1>



1.      Do not bind the function in the view. Instead evaluate in controller and bind it.

<div> {{ getUserName()}} </div>




4. $watchCollection and $watch

$watch() function allow us to create custom watchers in angular application to manipulate the data on change. By default the deep checking of the reference would not be done on $digest cycle.

The $watch() function takes a third, optional argument for "object equality." If you pass-in "true" for this argument, AngularJS will actually perform a deep-object-tree comparison. This means that within each $digest, AngularJS will check to see if the new and old values have the same structure (not just the same physical reference).

This causes app to track larger foot print; however, the deep object tree comparison is far more computationally expensive. Instead we could use $watchCollection() to fulfill that scenario.


tick

cross

$watchCollection() works by comparing physical object references; however, unlike the $watch() function, the $watchCollection() goes one-level deep and performs an additional, shallow reference check of the top level items in the collection.

$scope.$watchCollection(
    "collection",
    function( newValue, oldValue ) {
       // Code on change
    }                                                
 );



$scope.$watch("collection",
     function( newValue, oldValue ) {
            // Code on change
     },
     true // Object.
     );



5. Limit Filter Usage      
        
Filters are powerful service provided in angular which transform your application data at runtime based on business need.
Even though filters are allowed to place it in the view Its good practice to transform the data in the controller applying
necessary filters before binding.

When filters are applied in the view with pipe symbol which in turn creates watchers. If digest cycle is triggered with
any changes to the model all the filters would execute.

This again adds heavy lifting to angular engine to keep track and run filter function on each digest cycle.








tick

cross

Translating in controller and bind it.

$scope.description = $translate.instant(‘Title Description’);


– In HTML {{::description}}


Avoid binding filter in the view. This filter will run through each digest cycle irrespective of changes.


$scope.decription = ‘Title Description’;

{{ description | translate}}




6. Disable debug info in production    

Angular provides all the necessary information for debugging along with other features. This debugging supported through adding some addition properties and classes to the DOM.  Manipulating attributes and classes will also cost to performance
and expensive operation on DOM elements.

We need to turn off this debug flag while distributing the code to production.


tick




app.config(['$compileProvider', function ($compileProvider) {

// disable debug info
  $compileProvider.debugInfoEnabled(false);
}]);





7. $digest over $apply  

$scope.$apply() is costly operation which typically trigger the digest cycle from root scope. Calling all the watchers to recheck and update the value.

We should use $scope.$digest() instead of $scope.$apply() when we know the $scope changes are needed to update in the
child node. Because $digest() will propagate the update downwards through child node from where it started. 
  

8. $interval and $timeout

Do not use the standard JavaScript setTimeout and setInterval function over Angular wrappers functions such as$timeout and $interval to avoid manually calling $scope.$apply().

These wrapper functions internally call $scope.$apply when handler is completed execution. One important thing to take
care of setting $interval or $timeout is setting optional invokeApplly parameter.

By default the third parameter is set to true which indicated angular engine to trigger $scope.$apply internally.
InvokeApplly parameter should set to false explicitly if there are no $scope updating is required.







tick

cross

$timeout(function(){
   // Snippet here will not
  // trigger digest   cycle
},
4000,
true);

// This function change the scope we leave the invokeApply to default value.


$interval(function(){
   $scope.name = ‘hello’;
},
4000);



function timerCallback(){
   // Snippet here not updating $scope
}


$timeout(timerCallback,4000,true);

$interval(timerCallback,4000,true);





9. Unregister Watchers and Destroy Polling

$watch() function is used to create custom watchers in application to do some operation based on model changes.
When this watch is registered and called it returns a callback function.

This function holds unregistering instruction for the respective watchers. Unregistering must be done calling this
function to avoid the memory leak.

Similarly $timeout and $interval ids must be cleared using $timeout.cancel() function after its usage and scope destroy.


No comments: