AngularJS is a powerful framework for building dynamic web applications, but as applications grow, performance can become an issue. Optimizing AngularJS applications ensures smooth user experiences and efficient resource utilization. This guide covers best practices for enhancing AngularJS performance.
1. Minimize Watchers and Digest Cycle
1.1 Understanding Watchers
AngularJS uses the $digest
cycle to detect changes in the application by checking all watchers. More watchers lead to longer digest cycles and slow performance.
1.2 How to Optimize Watchers
- Use one-time bindings (
::
) for static data:<h1>{{::title}}</h1>
- Avoid unnecessary
ng-repeat
expressions. - Reduce the number of bound DOM elements.
- Use
$scope.$watch
sparingly.
2. Optimize ng-repeat
2.1 Use track by for ng-repeat
When using ng-repeat
, AngularJS creates and destroys elements frequently, impacting performance. Using track by
helps Angular track elements efficiently.
<li ng-repeat="item in items track by item.id">{{ item.name }}</li>
2.2 Use Limitations on Large Data
For large datasets, consider paginating or limiting the number of displayed items.
<li ng-repeat="item in items | limitTo:10">{{ item.name }}</li>
3. Reduce DOM Manipulations
3.1 Use ng-if Instead of ng-show/ng-hide
ng-show
and ng-hide
keep elements in the DOM, whereas ng-if
removes them when not needed.
<!-- Inefficient -->
<div ng-show="isVisible">Visible Element</div>
<!-- Efficient -->
<div ng-if="isVisible">Visible Element</div>
3.2 Use $compile Wisely
Manually compiling templates can slow down performance if overused. Avoid using $compile
inside loops.
4. Optimize Scope Usage
4.1 Avoid Deep Watchers
AngularJS deep-watches objects, making the digest cycle slower. Use shallow watchers where possible.
$scope.$watch('user', function(newVal, oldVal) {
// Avoid deep watching
}, true);
Instead, watch specific properties:
$scope.$watch('user.name', function(newVal, oldVal) {
// More efficient
});
4.2 Unbind Watchers When Not Needed
Remove watchers when they are no longer needed using $destroy
.
var unwatch = $scope.$watch('someProperty', function(newVal, oldVal) {
console.log(newVal);
});
// Unbind when done
$scope.$on('$destroy', function() {
unwatch();
});
5. Lazy Load Modules and Components
5.1 Load Dependencies on Demand
Use lazy loading for controllers and modules to avoid loading everything at once.
var app = angular.module("myApp", ["oc.lazyLoad"]);
app.config(["$ocLazyLoadProvider", function($ocLazyLoadProvider) {
$ocLazyLoadProvider.config({
debug: true
});
}]);
app.controller("MainController", function($scope, $ocLazyLoad) {
$scope.loadModule = function() {
$ocLazyLoad.load("someModule.js");
};
});
6. Use Bind Once (::
) for Static Data
For data that does not change, use one-time bindings:
<h1>{{::title}}</h1>
This prevents Angular from re-evaluating the expression during the digest cycle.
7. Optimize API Calls and Data Binding
7.1 Use Service Instead of $scope for Data Storage
If data is used in multiple controllers, use a service instead of $scope
variables.
app.service("DataService", function() {
var data = {};
return {
getData: function() { return data; },
setData: function(newData) { data = newData; }
};
});
7.2 Use $http with Caching
Enable caching for repeated API calls:
app.service("ApiService", function($http) {
this.getData = function() {
return $http.get("https://api.example.com/data", { cache: true });
};
});
7.3 Debounce API Calls
If making frequent API calls, use debounce techniques:
app.controller("SearchController", function($scope, $timeout) {
var timeout = null;
$scope.search = function(query) {
if (timeout) $timeout.cancel(timeout);
timeout = $timeout(function() {
// Make API call here
console.log("Fetching data for", query);
}, 300);
};
});
8. Optimize Images and Assets
- Use lazy loading for images.
- Optimize image sizes using tools like TinyPNG.
- Use CDN for static assets.
<img data-src="image.jpg" class="lazyload">
Use libraries like LazyLoad.js to load images only when they appear in the viewport.
9. Use AngularJS Batching
Batch multiple updates inside $applyAsync
to prevent multiple digest cycles.
$scope.$applyAsync(function() {
$scope.data = newData;
});
10. Enable Performance Debugging
Use AngularJS Batarang (Chrome Extension) to analyze watchers and performance bottlenecks.
Run this in the browser console to check the number of watchers:
(function() {
var root = angular.element(document.getElementsByTagName("body"));
var 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);
console.log("Total Watchers: ", watchers.length);
})();