AngularJS, being one of the most famous modern web application frameworks, provides us various magic features like two-way binding, maintainability, and reusability of code via directives, services, filters etc. Angular applications, however, start suffering from performance problems as they grow in size & complexity. For larger applications, application performance shows crawling behaviour if these features are not handled with care.
Even though Angular 2 started being popular and faster than AngularJS 1.x series these days, all existing AngularJS applications would surely not afford code migration to be the solution over performance issues. Rather, trying other ways would be better option than complete re-write.
Few key solutions are suggested below to achieve optimal application speed & performance.
Angular1.3 has provided one time binding feature with :: notation. With this amazing feature, Angular will wait for values which are specified using one time binding notation to get stable, after running first series of digest cycles, and will use those values to render the DOM element. After that, Angular will remove the watchers associated with those values forgetting about that binding. Thus, it saves time and efforts taken for Angular's two-way binding.
Minimizing $rootScope level data
In an Angular application, $rootScope object is the closest object to application's global context. Thus, we should not attach too much logic to this global context. $rootScope default properties, functions can be identified with $ notation used in their naming. The objects, functions that we have added explicitly to $rootScope can be separated from defaults and hence, exact rootScope data size can be computed. Further, the use of angular services can be done to share commonly used data among different controllers instead of keeping it centrally in $rootScope.
Destroying of object/function data bound to DOM after its use to avoid memory leaks
Use of destroy event at scope level and element level will help us to achieve this. For scenario, when you need to free resources, prefer use of on $destroy, which removes event listeners, which were set using addEventListener (). Also, in scenarios where use of setTimeout or setInterval calls, those are not tracked by Angular as $timeout and $interval services, thus can lead to memory leaks. In such a case, we should not call $destroy, but should catch $destroy event by calling $scope.$on('$destroy', function(){}); in order to release all significant resources inside function.
Make use of ng-model-options ="{updateOn: ‘blur’}" wherever possible as per your scenarios
For scenario, where various functions are supposed to be called on model change event, while using angular on-change event i.e. ng-change in-built directive, make use of ng-model-options="{updateOn: ‘blur’}" wherever possible as per your scenarios. This will trigger the change event on complete ng-model update.
Avoid passing functions/scope methods as parameters to custom directive attributes especially if directives are called inside loops such as ng-repeat etc.
Instead, bind objects directly if possible or put such function calls for dynamic objects on ng-init like directive that is called only once.
Avoid applying multiple filters especially inside loops such as ng-repeat or nested ng-repeat loops
Instead, apply them inside controller function and bind filtered objects to UI. This will prevent triggering of filter events/methods multiple times.
Disabling Debug Data using $compileProvider.debugInfoEnabled method
In AngularJS applications, by default, AngularJS attaches information about binding information and a reference to the current scope/scopes isolate scopes to DOM elements. In addition, it adds CSS classes to data-bound elements. Thus, as a result of ngBind,ngBindHtml etc. directives or interpolations, binding of data and CSS classes ,which we call ng-bindings are attached to the respective element. Various tools such as Batarang or Protractor or other debugging tools make use of this information to help us debug our application. Once our application has been tested as well as runs successfully, addition of all this extra information results in slowness of application. To optimize/boost up our application's performance, we can disable the debug information in production.
Example: -
var app = angular.module("myAngularApp", [ ] );
app.config ( [ '$compileProvider', function
($compileProvider) { $compileProvider.debugInfoEnabled(false);
}]);
Prefer Using $watchCollection instead of $watch with a third parameter i.e. objectEquality
$watch with only two parameters, which are watchExpression & listener respectively, is comparatively fast as it only checks object reference equality. This means that within each $digest, AngularJS will check to see if the new and old values are the same "physical" object. (Object reference check). However, for $watch with a third parameter, the3rd objectEquality parameter, tells Angular to perform a deep-object-tree comparison. Within each $digest cycle, AngularJS will check if the new and old values have the same structure that is to check every property of the object along with physical object reference. However, this deep-down object comparison is computationally very expensive.
$watchCollection acts as mid-ground between the two $watch configurations above. This means $watchCollection () checks in more depth than $watch with two parameters but not as much expensive computing as for $watch with thirdparameter. WatchCollection () performs shallow reference check, thus we can better use watchCollection as per application requirements and scenarios.
Bind objects/variables/methods to controller scope only if they are accessed or used in respective controller's views i.e. UI/HTML
Make use of service/factory methods rather than writing anonymous functions in controller. This will help to optimize data bound to controller scope.
Prefer using ng-if directive instead of ng-show/ng-hide directives, as per considering your scenarios
Both ng-if & ng-show/hide take Boolean value as condition. So, ng-show directive will render an element when condition supplied is true, or else use CSS property 'display: none' to hide it. ng-if directive will remove the element from DOM when condition gets false, and will re-create it when condition gets true. That means ng-show/ng-hide allows the DOM element be alive and simply to be hidden using CSS, thus all bindings, watch expression evaluations and performance cost associated to it will be still there. This can be optimized if we prefer use of ng-if directive, but we must consider, if removal & recreation of DOM element actually suits our scenario.
If you have any suggestions regarding this article, please comment below. Thank you for reading. I hope that while building Angular JS application, consideration of above factors, will help you improve your application speed & performance.