Performance Tuning AngularJS Apps
26th Jun 2015
For existing AngularJs apps, there are a few things that you can do in order to try and improve performance.
Destroy no longer needed javascript plugin elements
This should help prevent your Angular App from running into memory problems in the browser. There are basically two approaches to this.
In directives that wrap some sort of plugin (e.g. Slick Slider), you need to listen out for the "$destroy" event and call that particular plugin's cleanup methods. In the case of Slick Slider, it's the unslick() method, but it could simply be a call to jQuery's remove() method, or you could just set the value of the html element to an empty string:
$scope.$on('$destroy', function() {
// Call the plugin's own api
$('.slick-slider').unslick();
// or call jQuery's remove function
$(element).remove();
// or, if you aren't using jQuery
element.html("");
});
Unbind any watches when the current scope is destroyed
When you create a watch on a scoped variable, or on an event in angular, the $watch function returns a function that when called, will remove the watch. Call this returned function when your scope is destroyed as it is no longer needed:
var unbindCompanyIdWatch = $scope.$watch('companyId', () {
// Do Something...
});
$scope.$on('$destroy', function() {
unbindCompanyIdWatch();
});
Use One-Time Binding where possible
The less binding going on, the less watchers there are.
If you render values in your dom using angular that you know are only going to be set once and will never change during the lifecycle of the page, it is a candidate for using one-time binding. The One-Time binding syntax is basically two colons - "::", and can be used a few ways in your html templates:
<!-- Basic One-Time Binding -->
<p>
{ {:: test } }
</p>
<!-- Within ng-repeat -->
<ul>
<li ng-repeat="item in ::items">
{ {::item.name} }
</li>
</ul>
<!-- Within ng-show -->
<p ng-show="::showContent"> Some Content </p>
<!-- Within ng-if -->
<p ng-if="::showContent"> Some Content </p>
Use "track by" when using ng-repeat where possible
By specifying a property for angular to track an item within a collection by, you will prevent angular from rebuilding entire chunks of the dom unnecessarily. This will give you a performance boost which will be noticeable when dealing with large collections:
<ul>
<li ng-repeat="item in items track by item.itemId">
{ {item.name} }
</li>
</ul>
Ben Nadel has an excellent post on track by that you should checkout.
Of course, you shouldn't need to tie this up with some one-way binding, as track by would be pointless with a collection that does not need to change.
Cancel no longer required Http requests
If some action happens that means that data that is loading is no longer needed (e.g. a navigation change), you should cancel the no longer required http requests. Most browsers limit the number of concurrent requests to a single domain. So, if your requests are no longer required, cancel them and free up those request slots.
You can do this by simply resolving the promise. Your requirements of when this cancellation needs to happen will be different for every app, so I would recommend that you write a httpRequestManagerService, and marshal any http requests through it that you deem necessary. You can then resolve your promises based on some event - e.g. a navigation change event. Ben Nadel has a good post on cancelling angular requests.
Interchange ng-if and ng-show where appropriate
On the surface, ng-show and ng-if produce the same result. However, under the hood, they behave slightly differently.
ng-show still renders your html no matter what. If it does not need to be shown, the html element with the ng-show directive will simply be marked with a display:none css style.
ng-if will completely remove the html and all children that contain the directive.
There is obviously a cost in completely removing and adding entire chunks of html, however if you are dealing with a lot of html, or if your html within your ng-if contains a lot of angular expressions, I have found it to be more performant than ng-show.
My advice is to evaluate both in each case before making a decision.
Please feel free to throw in any more performance tips in the comments