Reputation: 469
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:
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:
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.
Upvotes: 42
Views: 27852
Reputation: 617
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
Upvotes: 6
Reputation: 1919
Just like in any other performance optimization, it is important to know how to profile the application to find the true bottleneck. Then you can solve them one by one. I usually fight the bottlenecks in the following order:
I have profiled an angular example step by step showing how to identify a bottleneck at each step. http://bahmutov.calepin.co/improving-angular-web-app-performance-example.html
Upvotes: 0
Reputation: 3493
try avoiding the following
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
where ever possible use scope.$digest instead of scope.$watch, this ensures that digest cycle is executed only on the child scopes
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
Upvotes: 2
Reputation: 192
This will only be a link! This is just an idea I had when I was reading this, I haven't explored this yet but someone probabbly did so I'm waiting for their reply on my idea. How about using shared web workers to get lots of heavy processing out of the ui thread? https://github.com/h2non/sharedworkers-angular-poc
The other idea I had was a simpler one. Would your app benefit from infinite scrolling? I mean these forms probabbly dont all fit on the screen, they are not connected to eachother so why not draw them as they are needed? Load them up in memory then draw them accordingly.
Upvotes: 0
Reputation: 7286
A middle ground between moving the DOM manipulation into custom directives and the $watch problems with lots of $watches is to use "bind-once" semantics.
This is great for data that is immutable once the data is made available. See bindonce
Upvotes: 1
Reputation: 4102
Am sorry for putting it as an 'answer' because i do not have enough points yet to make a comment.
We have run into similar issues with our AngularJS app. Using 'batarang' it seems having to deal with a large number of scope objects and their relevant $watch expressions creates a performance hiccup. This has set us wondering if another framework or something like ReactJS should be used instead to take care of the 'view' part.
Upvotes: 2
Reputation: 3856
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 :)
Upvotes: 21
Reputation: 1189
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.
Upvotes: 8
Reputation: 12630
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
Upvotes: 3