AngularJS


Performance All Versions

0.9.0
0.9.1
0.9.2
0.9.3
0.9.4
0.9.5
0.9.6
0.9.7
0.9.9
0.9.10
0.9.11
0.9.12
0.9.13
0.9.14
0.9.15
0.9.16
0.9.17
0.9.18
0.9.19
0.10.0
0.10.1
0.10.2
0.10.3
0.10.4
0.10.5
0.10.6
1.0.0rc1
g3-v1.0.0rc1
g3-v1.0.0-rc2
v1.0.0rc2
v1.0.0rc3
v1.0.0rc4
v1.0.0rc5
v1.0.0rc6
v1.0.0rc7
v1.0.0rc8
v1.0.0rc9
v1.0.0rc10
v1.0.0rc11
v1.0.0rc12
1.0.0
1.0.1
1.0.2
1.1.0
1.0.3
1.1.1
1.0.4
1.1.2
1.0.5
1.1.3
1.0.6
1.1.4
1.0.7
1.1.5
1.2.0rc1
1.0.8
1.2.0-rc.2
1.2.0-rc.3
1.2.0
1.2.1
1.2.2
1.2.3
1.2.4
1.2.5
1.2.6
1.2.7
1.2.8
1.2.9
1.2.10
1.2.11
1.2.12
1.2.13
1.2.14
1.3.0-beta.1
1.3.0-beta.2
1.3.0-beta.3
1.2.15
1.3.0-beta.4
1.2.16
1.3.0-beta.5
1.3.0-beta.6
1.3.0-beta.7
1.3.0-beta.8
1.3.0-beta.9
1.3.0-beta.10
1.2.17
1.3.0-beta.11
1.2.18
1.3.0-beta.12
1.3.0-beta.13
1.2.19
1.3.0-beta.14
1.2.20
1.3.0-beta.15
1.3.0-beta.16
1.2.21
1.3.0-beta.17
1.2.22
1.3.0-beta.18
1.2.23
1.3.0-beta.19
1.3.0-rc.0
1.2.24
1.3.0-rc.1
1.2.25
1.3.0-rc.2
1.3.0-rc.3
1.3.0-rc.4
1.2.26
1.3.0-rc.5
1.3.0
1.3.1
1.3.2
1.3.3
1.2.27
1.3.4
1.3.5
1.3.6
1.3.7
1.2.28
1.3.8
1.4.0-beta.0
1.3.9
1.3.10
1.4.0-beta.1
1.3.11
1.4.0-beta.2
1.3.12
1.4.0-beta.3
1.3.13
1.4.0-beta.4
1.3.14
1.4.0-beta.5
1.3.15
1.4.0-beta.6
1.4.0-rc.0
1.4.0-rc.1
1.4.0-rc.2
1.4.0
1.3.16
1.4.1
1.3.17
1.4.2
1.4.3
1.4.4
1.3.18
1.4.5
1.3.19
1.4.6
1.5.0-beta.0
1.2.29
1.3.20
1.4.7
1.5.0-beta.1
1.5.0-beta.2
1.4.8
1.5.0-rc.0
1.5.0-rc.1
1.4.9
1.5.0-rc.2
1.5.0
1.4.10
1.5.1
1.5.2
1.5.3
1.5.4
1.5.5
1.4.11
1.5.6
1.4.12
1.5.7
1.2.30
1.5.8

Improvements requested by Ashwin Ramaswami:

  • This topic duplicates material from other topics, is deprecated, is obsolete, or is otherwise superfluous. - Aug 6 at 12:33
    Comments:
    • The "6 simple performance improvements" section duplicates lots of material from the other topics below. - Ashwin Ramaswami

This draft deletes the entire topic.

inline side-by-side expand all collapse all

Examples

  • 66

    1) Use ng-repeat sparingly

    Using ng-repeat in views generally results in poor performance, particularly when there are nested ng-repeat's

    This is super slow!

    <div ng-repeat="user in userCollection">
      <div ng-repeat="details in user">
        {{details}}
      </div>
    </div>
    

    Try to avoid nested repeats as much as possible. One way to improve the performance of ng-repeat is to use track by $index (or some other id field). By default, ng-repeat tracks the whole object. With track by, Angular watches the object only by the $index or object id.

    <div ng-repeat="user in userCollection track by $index">
      {{user.data}}
    </div>
    

    Use other approaches like pagination, virtual scrolls or infinite scrolls whenever possible to avoid iterating over large collections.

    2) Bind once

    Angular has bidirectional data binding. It comes with a cost of being slow if used too much.

    Slower Performance

    <!-- Default data binding has a performance cost -->
    <div>{{ my.data }}</div>
    

    Faster Performance (AngularJS >= 1.3)

    <!-- Bind once is much faster -->
    <div>{{ ::my.data }}</div>
    

    Using the "bind once" notation tells Angular to wait for the value to stabilize after the first series of digest cycles. Angular will use that value in the DOM, then remove all watchers so that it becomes a static value and is no longer bound to the model.

    3) Scope functions and filters take time

    AngularJS has a digest loop. All your functions are in a view and filters are executed every time the digest cycle runs. The digest loop will be executed whenever the model is updated and it can slow down your app (filter can be hit multiple times before the page is loaded).

    Avoid this:

    <div ng-controller="bigCalulations as calc">
      <p>{{calc.calculateMe()}}</p>
      <p>{{calc.data | heavyFilter}}</p>
    </div>
    

    Better approach

    <div ng-controller="bigCalulations as calc">
      <p>{{calc.preCalculatedValue}}</p>
      <p>{{calc.data | lightFilter}}</p>
    </div>
    

    Where the controller can be:

    .controller('bigCalulations', function(valueService) {
      // bad, because this is called in every digest loop
      this.calculateMe = function() {
        var t = 0;
        for(i = 0; i < 1000; i++) {
          t += i;
        }
        return t;
      }
      // good, because this is executed just once and logic is separated in service to    keep the controller light
      this.preCalulatedValue = valueService.valueCalculation(); // returns 499500
    });
    

    4) Watchers

    Watchers tremendously drop performance. With more watchers, the digest loop will take longer and the UI will slow down. If the watcher detects change, it will kick off the digest loop and re-render the view.

    There are three ways to do manual watching for variable changes in Angular.

    $watch() - watches for value changes

    $watchCollection() - watches for changes in collection (watches more than regular $watch)

    $watch(..., true) - Avoid this as much as possible, it will perform "deep watch" and will decline the performance (watches more than watchCollection)

    Note that if you are binding variables in the view you are creating new watches - use {{::variable}} to prevent creating a watch, especially in loops.

    As a result you need to track how many watchers you are using. You can count the watchers with this script (credit to @Words Like Jared Number of watchers)

    (function() {
      var root = angular.element(document.getElementsByTagName('body')),
          watchers = [];
    
      var f = function(element) {
    
        angular.forEach(['$scope', '$isolateScope'], function(scopeProperty) {
          if(element.data() && element.data().hasOwnProperty(scopeProperty)) {
            angular.forEach(element.data()[scopeProperty].$$watchers, function(watcher) {
              watchers.push(watcher);
            });
          }
        });
    
        angular.forEach(element.children(), function(childElement) {
          f(angular.element(childElement));
        });
    
      };
     
      f(root);
     
      // Remove duplicate watchers
      var watchersWithoutDuplicates = [];
      angular.forEach(watchers, function(item) {
        if(watchersWithoutDuplicates.indexOf(item) < 0) {
          watchersWithoutDuplicates.push(item);
        }
      });
    
      console.log(watchersWithoutDuplicates.length);
    
    })();
    

    5) ng-if / ng-show

    These functions are very similar in behavior. ng-if removes elements from the DOM while ng-show only hides the elements but keeps all handlers. If you have parts of the code you do not want to show, use ng-if.

    It depends on the type of usage, but often one is more suitable than the other.

    • If the element is not needed, use ng-if

    • To quickly toggle on/off, use ng-show/ng-hide

      <div ng-repeat="user in userCollection">
        <p ng-if="user.hasTreeLegs">I am special<!-- some complicated DOM --></p>
        <p ng-show="user.hasSubscribed">I am awesome<!-- switch this setting on and off --></p>
      </div>
      

    If in doubt - use ng-if and test!

    6) Disable debugging

    By default, bind directives and scopes leave extra classes and markup in the code to assist with various debugging tools. Disabling this option means that you no longer render these various elements during the digest cycle.

    angular.module('exampleApp', [])
      .config(['$compileProvider', function ($compileProvider) {
        $compileProvider.debugInfoEnabled(false);
      }]);
    

    7) Use dependency injection to expose your resources

    Dependency Injection is a software design pattern in which an object is given its dependencies, rather than the object creating them itself. It is about removing the hard-coded dependencies and making it possible to change them whenever needed.

    You might wonder about the performance cost associated with such string parsing of all injectable functions. Angular takes care of this by caching the $inject property after the first time. So this doesn’t happen everytime a function needs to be invoked.

    PRO TIP: If you are looking for the approach with the best performance, go with the $inject property annotation approach. This approach entirely avoids the function definition parsing because this logic is wrapped within the following check in the annotate function: if (!($inject = fn.$inject)). If $inject is already available, no parsing required!

    var app = angular.module('DemoApp', []);
    
    var DemoController = function (s, h) {
        h.get('https://api.github.com/users/angular/repos')
            .success(function (repos) {
            s.repos = repos;
        });
    }
    // $inject property annotation
    DemoController['$inject'] = ['$scope', '$http'];
    
    app.controller('DemoController', DemoController);
    
  • 16

    Angular has reputation of having awesome bidirectional data binding. It comes with a cost of being a bit slow, if used too much.

    This will have two way data binding, and will have larger performance hit.

    Bad performance: {{my.data}}

    Add two colons :: before the variable name to use uni-directional binding. In this case, the value only gets updated once my.data is defined. You are explicitly pointing not to watch for data changes. Angular won't perform any value checks, resulting with fewer expressions being evaluated on each digest cycle.

    Good performance examples using one-time binding

    {{::my.data}}
    <span ng-bind="::my.data"></span>
    <span ng-if="::my.data"></span>
    <span ng-repeat="item in ::my.data">{{item}}</span>
    <span ng-class="::{ 'my-class': my.data }"></div>
    
  • 14

    These functions are very similar in behaviour. The difference is that ng-if removes elements from the DOM. If there are large parts of the code that will not be shown, then ng-if is the way to go. ng-show will only hide the elements but will keep all the handlers.

    ng-if

    The ngIf directive removes or recreates a portion of the DOM tree based on an expression. If the expression assigned to ngIf evaluates to a false value then the element is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.

    ng-show

    The ngShow directive shows or hides the given HTML element based on the expression provided to the ngShow attribute. The element is shown or hidden by removing or adding the ng-hide CSS class onto the element.

    Example

    <div ng-repeat="user in userCollection">
      <p ng-if="user.hasTreeLegs">I am special
        <!-- some complicated DOM -->
      </p>
      <p ng-show="user.hasSubscribed">I am aweosme
        <!-- switch this setting on and off -->
      </p>
    </div>
    

    Conclusion

    It depends from the type of usage, but often one is more suitable than the other (e.g., if 95% of the time the element is not needed, use ng-if; if you need to toggle the DOM element's visibility, use ng-show).

    When in doubt, use ng-if and test!

    Note: ng-if creates a new isolated scope, whereas ng-show and ng-hide don't. Use $parent.property if parent scope property is not directly accessible in it.

I am downvoting this example because it is...

Syntax

Syntax

Parameters

Parameters

Remarks

Remarks

Still have a question about Performance? Ask Question

6 Simple Performance Improvements

66

1) Use ng-repeat sparingly

Using ng-repeat in views generally results in poor performance, particularly when there are nested ng-repeat's

This is super slow!

<div ng-repeat="user in userCollection">
  <div ng-repeat="details in user">
    {{details}}
  </div>
</div>

Try to avoid nested repeats as much as possible. One way to improve the performance of ng-repeat is to use track by $index (or some other id field). By default, ng-repeat tracks the whole object. With track by, Angular watches the object only by the $index or object id.

<div ng-repeat="user in userCollection track by $index">
  {{user.data}}
</div>

Use other approaches like pagination, virtual scrolls or infinite scrolls whenever possible to avoid iterating over large collections.

2) Bind once

Angular has bidirectional data binding. It comes with a cost of being slow if used too much.

Slower Performance

<!-- Default data binding has a performance cost -->
<div>{{ my.data }}</div>

Faster Performance (AngularJS >= 1.3)

<!-- Bind once is much faster -->
<div>{{ ::my.data }}</div>

Using the "bind once" notation tells Angular to wait for the value to stabilize after the first series of digest cycles. Angular will use that value in the DOM, then remove all watchers so that it becomes a static value and is no longer bound to the model.

3) Scope functions and filters take time

AngularJS has a digest loop. All your functions are in a view and filters are executed every time the digest cycle runs. The digest loop will be executed whenever the model is updated and it can slow down your app (filter can be hit multiple times before the page is loaded).

Avoid this:

<div ng-controller="bigCalulations as calc">
  <p>{{calc.calculateMe()}}</p>
  <p>{{calc.data | heavyFilter}}</p>
</div>

Better approach

<div ng-controller="bigCalulations as calc">
  <p>{{calc.preCalculatedValue}}</p>
  <p>{{calc.data | lightFilter}}</p>
</div>

Where the controller can be:

.controller('bigCalulations', function(valueService) {
  // bad, because this is called in every digest loop
  this.calculateMe = function() {
    var t = 0;
    for(i = 0; i < 1000; i++) {
      t += i;
    }
    return t;
  }
  // good, because this is executed just once and logic is separated in service to    keep the controller light
  this.preCalulatedValue = valueService.valueCalculation(); // returns 499500
});

4) Watchers

Watchers tremendously drop performance. With more watchers, the digest loop will take longer and the UI will slow down. If the watcher detects change, it will kick off the digest loop and re-render the view.

There are three ways to do manual watching for variable changes in Angular.

$watch() - watches for value changes

$watchCollection() - watches for changes in collection (watches more than regular $watch)

$watch(..., true) - Avoid this as much as possible, it will perform "deep watch" and will decline the performance (watches more than watchCollection)

Note that if you are binding variables in the view you are creating new watches - use {{::variable}} to prevent creating a watch, especially in loops.

As a result you need to track how many watchers you are using. You can count the watchers with this script (credit to @Words Like Jared Number of watchers)

(function() {
  var root = angular.element(document.getElementsByTagName('body')),
      watchers = [];

  var f = function(element) {

    angular.forEach(['$scope', '$isolateScope'], function(scopeProperty) {
      if(element.data() && element.data().hasOwnProperty(scopeProperty)) {
        angular.forEach(element.data()[scopeProperty].$$watchers, function(watcher) {
          watchers.push(watcher);
        });
      }
    });

    angular.forEach(element.children(), function(childElement) {
      f(angular.element(childElement));
    });

  };
 
  f(root);
 
  // Remove duplicate watchers
  var watchersWithoutDuplicates = [];
  angular.forEach(watchers, function(item) {
    if(watchersWithoutDuplicates.indexOf(item) < 0) {
      watchersWithoutDuplicates.push(item);
    }
  });

  console.log(watchersWithoutDuplicates.length);

})();

5) ng-if / ng-show

These functions are very similar in behavior. ng-if removes elements from the DOM while ng-show only hides the elements but keeps all handlers. If you have parts of the code you do not want to show, use ng-if.

It depends on the type of usage, but often one is more suitable than the other.

  • If the element is not needed, use ng-if

  • To quickly toggle on/off, use ng-show/ng-hide

    <div ng-repeat="user in userCollection">
      <p ng-if="user.hasTreeLegs">I am special<!-- some complicated DOM --></p>
      <p ng-show="user.hasSubscribed">I am awesome<!-- switch this setting on and off --></p>
    </div>
    

If in doubt - use ng-if and test!

6) Disable debugging

By default, bind directives and scopes leave extra classes and markup in the code to assist with various debugging tools. Disabling this option means that you no longer render these various elements during the digest cycle.

angular.module('exampleApp', [])
  .config(['$compileProvider', function ($compileProvider) {
    $compileProvider.debugInfoEnabled(false);
  }]);

7) Use dependency injection to expose your resources

Dependency Injection is a software design pattern in which an object is given its dependencies, rather than the object creating them itself. It is about removing the hard-coded dependencies and making it possible to change them whenever needed.

You might wonder about the performance cost associated with such string parsing of all injectable functions. Angular takes care of this by caching the $inject property after the first time. So this doesn’t happen everytime a function needs to be invoked.

PRO TIP: If you are looking for the approach with the best performance, go with the $inject property annotation approach. This approach entirely avoids the function definition parsing because this logic is wrapped within the following check in the annotate function: if (!($inject = fn.$inject)). If $inject is already available, no parsing required!

var app = angular.module('DemoApp', []);

var DemoController = function (s, h) {
    h.get('https://api.github.com/users/angular/repos')
        .success(function (repos) {
        s.repos = repos;
    });
}
// $inject property annotation
DemoController['$inject'] = ['$scope', '$http'];

app.controller('DemoController', DemoController);

Bind Once

16

Angular has reputation of having awesome bidirectional data binding. It comes with a cost of being a bit slow, if used too much.

This will have two way data binding, and will have larger performance hit.

Bad performance: {{my.data}}

Add two colons :: before the variable name to use uni-directional binding. In this case, the value only gets updated once my.data is defined. You are explicitly pointing not to watch for data changes. Angular won't perform any value checks, resulting with fewer expressions being evaluated on each digest cycle.

Good performance examples using one-time binding

{{::my.data}}
<span ng-bind="::my.data"></span>
<span ng-if="::my.data"></span>
<span ng-repeat="item in ::my.data">{{item}}</span>
<span ng-class="::{ 'my-class': my.data }"></div>

ng-if vs ng-show

14

These functions are very similar in behaviour. The difference is that ng-if removes elements from the DOM. If there are large parts of the code that will not be shown, then ng-if is the way to go. ng-show will only hide the elements but will keep all the handlers.

ng-if

The ngIf directive removes or recreates a portion of the DOM tree based on an expression. If the expression assigned to ngIf evaluates to a false value then the element is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.

ng-show

The ngShow directive shows or hides the given HTML element based on the expression provided to the ngShow attribute. The element is shown or hidden by removing or adding the ng-hide CSS class onto the element.

Example

<div ng-repeat="user in userCollection">
  <p ng-if="user.hasTreeLegs">I am special
    <!-- some complicated DOM -->
  </p>
  <p ng-show="user.hasSubscribed">I am aweosme
    <!-- switch this setting on and off -->
  </p>
</div>

Conclusion

It depends from the type of usage, but often one is more suitable than the other (e.g., if 95% of the time the element is not needed, use ng-if; if you need to toggle the DOM element's visibility, use ng-show).

When in doubt, use ng-if and test!

Note: ng-if creates a new isolated scope, whereas ng-show and ng-hide don't. Use $parent.property if parent scope property is not directly accessible in it.

Watchers

5

Watchers needed for watch some value and detect that this value is changed.

After call $watch() or $watchCollection new watcher add to internal watcher collection in current scope.

So, what is watcher?

Watcher is a simple function, which is called on every digest cycle, and returns some value. Angular checks the returned value, if it is not the same as it was on the previous call - a callback that was passed in second parameter to function $watch() or $watchCollection will be executed.

(function() {
  angular.module("app", []).controller("ctrl", function($scope) {
    $scope.value = 10;
    $scope.$watch(
      function() { return $scope.value; },
      function() { console.log("value changed"); }
    );
  }
})();

Watchers are performance killers. The more watchers you have, the longer they take to make a digest loop, the slower UI. If a watcher detects changes, it will kick off the digest loop (recalculation on all screen)

There are three ways to do manual watch for variable changes in Angular.

$watch() - just watches for value changes

$watchCollection() - watches for changes in collection (watches more than regular $watch)

$watch(..., true) - Avoid this as much as possible, it will perform "deep watch" and will kill the performance (watches more than watchCollection)

Note that if you are binding variables in the view, you are creating new watchers - use {{::variable}} not to create watcher, especially in loops

As a result you need to track how many watchers are you using. You can count the watchers with this script (credit to @Words Like Jared - How to count total number of watches on a page?

(function() {
  var root = angular.element(document.getElementsByTagName("body")),
      watchers = [];

  var f = function(element) {

    angular.forEach(["$scope", "$isolateScope"], function(scopeProperty) {
      if(element.data() && element.data().hasOwnProperty(scopeProperty)) {
        angular.forEach(element.data()[scopeProperty].$$watchers, function(watcher) {
          watchers.push(watcher);
        });
      }
    });

    angular.forEach(element.children(), function(childElement) {
      f(angular.element(childElement));
    });

  };

  f(root);

  // Remove duplicate watchers
  var watchersWithoutDuplicates = [];
  angular.forEach(watchers, function(item) {
    if(watchersWithoutDuplicates.indexOf(item) < 0) {
      watchersWithoutDuplicates.push(item);
    }
  });

  console.log(watchersWithoutDuplicates.length);

})();

If you don't want to create your own script, there is an open source utility called ng-stats that uses a real-time chart embedded into the page to give you insight into the number of watches Angular is managing, as well as the frequency and duration of digest cycles over time. The utility exposes a global function named showAngularStats that you can call to configure how you want the chart to work.

showAngularStats({
  "position": "topleft",
  "digestTimeThreshold": 16,
  "autoload": true,
  "logDigest": true,
  "logWatches": true
});

The example code above displays the following chart on the page automatically (interactive demo).

screenshot of ng-stats chart

Topic Outline