Angular JS Scaling & Performance

27,525

Solution 1

You need to create custom directives in order to curb the performance issues with angular. Unlike ember angular comes with all the bells and whistles turned on and it's up to you to tone it down. Here are a few directives I've created to help you out. Not all data in your app needs to be two way data bound and as a result you can save valuable cpu power by forgoing watch expressions in the page where needed. All of these directives bind data one time and leave it alone.

https://gist.github.com/btm1/6802599

https://gist.github.com/btm1/6802312

https://gist.github.com/btm1/6746150

One of the answers above talks about ng-repeat having huge performance hits so I give you "set-repeat" a one time data binding repeat directive :)

Solution 2

It is hard to provide a solution without more information about your problem, but I recently experienced (and solved) a performance issue that may be similar to what you saw, and was unrelated to the $digest cycle.

Most discussion of angularjs performance you will find (including the excellent post from Misko) is about the performance of dirty checking and the $digest cycle. But that is not the only performance issue you can experience with angularjs. The first step should be to determine if the digest cycle is your problem or not. For this, you can use batarang, or just look at your app and at when precisely it is sluggish. When the digest cycle is slow, essentially any interaction with the UI will be slow.

OTOH, you can have an app with a fast digest cycle, that is slow only when loading, switching views, or otherwise changing the sets of components to display, and this can manifest in profiling as a lot of time spent in parsing HTML and garbage collecting. In my case this was solved by doing some pre-computation of the html template to display, instead of relying on ng-repeat, ng-switch, ng-if everywhere.

I was using an ng-repeat="widget in widgets" containing an ng-switch on the type of widget, to display an arbitrary set of widgets (custom self-contained directives). Replacing this with code to generate the angular template for the specific set of widgets sped up route switching from ~10s to practically instant.

You can see the google groups thread above for a little more info on how I solved my particular problem, or provide more information about your application if you want some specific suggestions.

Solution 3

To improve performance in production read very nice one-liner below:

Quoting AngularJS Documentation:

By default AngularJS attaches information about binding and scopes to DOM nodes, and adds CSS classes to data-bound elements:

As a result of ngBind, ngBindHtml or {{...}} interpolations, binding data and CSS class ng-binding are attached to the corresponding element.

Where the compiler has created a new scope, the scope and either ng-scope or ng-isolated-scope CSS class are attached to the corresponding element. These scope references can then be accessed via element.scope() and element.isolateScope().

Tools like Protractor and Batarang need this information to run, but you can disable this in production for a significant performance boost with:

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

You can read more details here

Solution 4

Generally, AngularJS will perform poorly if there are more than 2000 data-bindings active, i.e. 2000 items in the scope that are being dirty-checked each $digest-cycle. Ng-repeat has a big performance impact because of this; each repeated items sets up at least two bindings, not counting any additional data or directives that are used inside the item.

One of the developers behind AngularJS gives an excellent description of the details of dirty-checking, and its performance in this SO answer:

https://stackoverflow.com/a/9693933/179024

The comment thread below that answer is worth a read, and I also share some thoughts about it in an answer further down on the same page:

https://stackoverflow.com/a/18381836/179024

Solution 5

try avoiding the following

  1. please avoid using ng-repeat if you have more than 50 elements in the list at a time and avoid manual watches
  2. do not use ng-click, ng-mouseenter,ng-mouseleave etc mouse events blindly till it is a dire need , try to reduce their numbers by using $event object along with event propagation concepts of js

  3. where ever possible use scope.$digest instead of scope.$watch, this ensures that digest cycle is executed only on the child scopes

    1. try having nested scopes i.e. one or two controllers inside one parent controller and keep the reusable logic in parent , i used this in nested states while using Ui-router (to fulfill a req where change of URL was required without page refresh ).

    most important! REMOVE ALL FILTERS FROM HTML!

all the above trigger a digest cycle on all the scopes of your application so there is a high probability that even when the view has been rendered angular is again executing relentless digest loops

Share:
27,525
Admin
Author by

Admin

Updated on July 28, 2022

Comments

  • Admin
    Admin almost 2 years

    We are pounding our heads against performance issues with an Angular app we are building for a bank.

    Unfortunately, it is a breach of contract to show snippets of the code. Regardless, I can describe some of the main issues going on, and I am hoping that best practice can be recommended.

    Applications Structure:

    • Essentially, a giant multi-form page.
    • Each form is its own partial, with nested controllers and partials about 3 levels deep.
    • The same forms are ng-repeated over a collection of json objects.
    • Each form is bound to the object / model that it is repeated over.
    • We are supposed to support anywhere from 1-200 forms on the page.

    If you take a look at the timeline. We are spending a great deal of time in the jQuery parse html method, jQuery recalculate stye method, the GC Event (Garbage Collection). I imagine minimizing these should speed things up a bit. They are all a part of the Angular lifecycle, but there may be better ways to avoid them. Here are some screenshots of the profiler:

    Recalculate Style GC Event

    Ultimately, the app is sluggish as the number of repeated forms goes above 5. Each form is relatively unrelated to the others. We have tried not to watch any shared properties between the forms.

  • DNS
    DNS almost 11 years
    I think Misko's answer, while great info, only partially addresses this question. A big part of the issue here appears to be that Angular is creating each element individually, rather than batching them into the same fragment.
  • MW.
    MW. almost 11 years
    The issue could simply be that with a number of repeated forms, each of which in istelf has a number of repeated items, the number of items being watched each time $digest runs causes the site to slow down. A good way to check this would be the batarang plugin for Chrome, which will show digest time in real-time. But it is kind of hard to pinpoint the issue as the OP can't show any code.
  • DNS
    DNS almost 11 years
    The OP does show the event timeline, and most of the shown activity is a long series of HTML parses. There may be a good reason why Angular is doing that versus creating them all in one fragment, but I think that's why it's slow here; the dirty-checking doesn't seem to be a big contributor.
  • jssebastian
    jssebastian almost 11 years
    I doubt that the html parsing above is happening inside the digest cycle, so this likely has nothing to do with $digest performance.
  • MW.
    MW. almost 11 years
    @jssebastian - No, this could indeed be unrelated to the $digest time. As noted, if the OP want to investigate it, I would recommend the Batarang-plugin.
  • Bradley Weston
    Bradley Weston about 10 years
    What would you use instead of ng-click and ng-repeat?
  • Rishul Matta
    Rishul Matta almost 10 years
    use javascript concepts of event capturing and bubbling to reduce ng-click and for ng-repeat use single binding property (newer versions of angular js)
  • TaylorMac
    TaylorMac over 9 years
    I wouldn't go against the grain unless you are having performance problems or know you will. Ex. If you don't use the built-in directives for events, if you want to update your models, you're stuck triggering .$digest() (which triggers dirty checking from the current scope downward) or worse $apply() (which triggers this from $rootScope), which is less efficient than using ng-click or others (which just registers another watcher for that scope).
  • TaylorMac
    TaylorMac over 9 years
    Removing all filters from HTML defeats the purpose of using filters, you might as well use a service instead. If you keep simple operations in filters (what they're intended for) you won't incur any noticeable performance hits.
  • Matheus
    Matheus over 9 years
    Were you still getting the 2 way data-binding with this approach?
  • jssebastian
    jssebastian about 8 years
    @matheus: yes, the contents of each widget still had two way binding. On the other hand, the list of widgets to display did not, it was generated by concatenating each individual widget's template.