Faster AngularJS Rendering (AngularJS and ReactJS)

Estimated reading time: 16 minutes

 

Have you used AngularJs and ran into some performance problems? Using ReactJs rendering becomes much faster. A small examples explains how to use ReactJs for rendering in AngularJs and a comparison is made between native rendering and rendering using ReactJs.

I like AngularJS. I use it when I do some little fun projects and I use it professionally in large Web apps. I tried other frameworks as well, like BackboneJS and EmberJS, which both are great tools as well. All three of them belong to the class of MVC frameworks (or MVVC whatever you want o call them). But whenever I used any of such a tool I always ran and still run into the same problem: Rendering performance of lists of items. Two way binding or one-way binding makes no real difference. For me BackboneJS had better performance for rendering than AngularJS. Lets put that on the back of two-way binding.

Now there is this great tool coming along called ReactJS. And this little innocent looking lib will definitely make one of the biggest changes in JavaScript MVC frameworks for the last couple of months (if not years). In short words, this library will make rendering finally fast. ReactJs calls itself the V in MVC (the View). At first I was wondering, why the hell would I want to drop the MC part? The MC part is where the fun is in all these frameworks like EmberJs, BackboneJS and AngularJS, right? After a closer look, you can actually see that ReactJs has quite some features of the C (controller) part as well, but yet ReactJs is not a full featured MVC framework and does not intend to be. The way ReactJs works is to keep a virtual DOM and render only what actually changes when the UI is updated?? Does that make sense, sounds new but so true. Send command to update UI, React compares these changes to existing DOM (using the Virtual DOM for ultra-fast diff) and React only updates what is needed. The cool stuff is actually that the the diff of changes can be performed very fast on the Virtual DOM. Facebook and Instagram have developed ReactJs and are using it in production.

I listened to a podcast with Pete Hunt, dev of ReactJ, where this guy talks about the intention of ReactJs. To resume in this place, but be sure to listen to this podcast, the intentions behind open sourcing ReactJs are make some noise in the V part of existing frameworks. And I think that existing frameworks are going to adopt the same or similar strategy for their UI part, or new libraries will come up that can be used.

As ReactJs is the V it can be easily as V in existing frameworks already today. BackboneJS , more, with AngularJS, ngReact  or here. Use it with Coffeescript? So you can use ReactJs to render those parts of your App where some performance comes in handy, for example ng-repeat with a few hundred items. In a previous post, I blogged how to make long lists ‘usable’ in AngularJs, but all these techniques to make “rendering” faster all rely on rendering only a part of the list. Using ReactJs with AngularJs, your rendering time will drop by about 80%. I played with ngReact and I experienced a performance increase that made me think something was wrong. Rendering time dropped from about 4200ms in native AngularJs to 120ms using ReactJs.  Go check ngReact out to try it yourself, or have a look at the plunkrs in this post.

I still experienced some issues when there are too many bindings in the DOM with AngularJS, but this is another problem that is specific to two-way binding. If you are too scared of this problem, you should probably go with another framework than AngularJs. ReactJs just helps us getting things as fast as possible on he screen of the user and here ReactJs is making a great job.

I will show a small example on how to use ReactJs to render the view for an Angular app. I installed bower and then

user@computer:$ mkdir fast-angular

cd fast-angular

bower install --save react

bower install --save angular

And then we are good to go. We only need ReactJs and AngularJs. We create a simple html file with both scripts loaded:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">

  </head>
  <body>
    <h1>Fast AngularJs</h1> <h3>AngularJs with ReactJs</h3>


    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/react/react.js"></script>

</body>
</html>

What we are going to create is a small ReactJs component that renders the string that we enter. So we use reactJs to render our model. We create a component called MYAPP, that renders a props that is passed to it. Then we create a traditional angularJs directive and controller (add the tags to our html, to start the app). The directive instead of rendering calls the ReactJs component and tells it to render. We use a $watch on framework to re-render on update. From the ReactJs docs calling createComponent when Component is already mounted updates the existing instance. (using React Chrome Dev integration tools, I keep on having the same instance ID, so I assume docs are true ) (See it live here http://plnkr.co/edit/FXK3lU?p=info)

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">

  </head>
  <body ng-app="fasterAngular">
    <h1>Fast AngularJs</h1> <h3>AngularJs with ReactJs</h3>
    <div ng-controller="mycontroller">
        <input ng-model="framework"/>
        <hr>
        <fast-ng framework="framework"></fast-ng>
        <hr>
        Rendering with traditional AngularJs {{framework}}
    </div>

    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/react/react.js"></script>
    <script >
        var MYAPP = React.createClass({
            displayName:'MYAPP',
            render:function(){
                return React.DOM.div(null, "Rendering faster in AngularJs with ", this.props.framework);

            }
        });
    </script>

    <script>
        angular.module('fasterAngular', []).
        controller('mycontroller', ['$scope', function($scope){
            $scope.framework = 'ReactJs';
 
        }]).directive('fastNg', function(){
            return{
                restrict:'E',
                scope:{
                    framework:'='
                },
                link:function(scope, el, attrs){
                    scope.$watch('framework', function(newValue, oldValue){
                        React.renderComponent(
                            MYAPP({framework:newValue}),
                            el[0]
                        );
                    })
                }
            }
        })
    </script>
</body>
</html>

Of course this simple example does not make us gain some performance, but It illustrates how we can use ReactJs to render our model. An other example that shows the performance is the next one, where we render a long list of numbers. The example is taken from the example of ngReact. We generate an Array with 1500 data entries and render it in a table. This is usually what brings some performance problems with native ng-repeat in AngularJs. See this Plunkr for rendering using ReactJs (http://plnkr.co/edit/ykYILa) and this one for native ngRepeat rendering (http://plnkr.co/edit/YnF7Vn). See code here, rendering with ReactJs

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">

  </head>
  <body ng-app="fasterAngular">
    <h1>Fast AngularJs</h1> <h3>AngularJs with ReactJs</h3>
    <div ng-controller="mycontroller">

        <fast-repeat data="data"></fast-repeat>

    </div>

    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/react/react.js"></script>

    <script >
        var MYLIST = React.createClass({displayName: 'MYLIST',
            render: function() {

              var data = this.props.data;

              var rows = data.map(function(datum) {
                var clickHandler = function(ev){
                    console.log("Still in reactJs");
                    console.log(ev);
                }

                return (
                  React.DOM.tr( {onClick:clickHandler},
                    React.DOM.td(null, datum['0']),
                    React.DOM.td(null, datum['1']),
                    React.DOM.td(null, datum['2']),
                    React.DOM.td(null, datum['3']),
                    React.DOM.td(null, datum['4'])
                  )
                );
              });

              return (
                React.DOM.table(null,
                  rows
                )
              );
            }
        });
    </script>
    <script>
        angular.module('fasterAngular', []).
        controller('mycontroller', ['$scope', function($scope){
            $scope.framework = 'ReactJs';
            $scope.data = [];
            // Fill the data map with random data
            for(var i = 0; i < 1500; ++i) {
                $scope.data[i] = {};
                for(var j = 0; j < 5; ++j) {
                    $scope.data[i][j] = Math.random();
                }
            }
        }]).directive('fastRepeat', function(){
            return{
                restrict: 'E',
                scope:{
                    data: '='
                },
                link:function(scope, el, attrs){
                    scope.$watch('data', function(newValue, oldValue){
                        React.renderComponent(
                            MYLIST({data:newValue}),
                            el[0]
                        );
                    })
                }
            }
        })
    </script>
</body>
</html>

A similar example where you can update the Data with a button Plunkr for Native Angular (http://plnkr.co/edit/YnF7Vn) and using ReactJs (http://plnkr.co/edit/6zfFXU)

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">

  </head>
  <body ng-app="fasterAngular">
    <h1>Fast AngularJs</h1> <h3>AngularJs with ReactJs</h3>
    <div ng-controller="mycontroller">
        <button ng-click="refresh()">Refresh Data</button>
        <fast-repeat data="data"></fast-repeat>
        <!-- <table>
          <tr ng-repeat="line in data" ng-click="clickHandler(ev)">
            <td>{{line[0]}}</td>
            <td>{{line[1]}}</td>
            <td>{{line[2]}}</td>
            <td>{{line[3]}}</td>
            <td>{{line[4]}}</td>
          </tr>
        </table> -->
    </div>

    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/react/react.js"></script>

    <script >
        var MYLIST = React.createClass({displayName: 'MYLIST',
            render: function() {

              var data = this.props.data;

              var rows = data.map(function(datum) {
                var clickHandler = function(ev){
                    console.log("Still in reactJs");
                    console.log(ev);
                }

                return (
                  React.DOM.tr( {onClick:clickHandler},
                    React.DOM.td(null, datum['0']),
                    React.DOM.td(null, datum['1']),
                    React.DOM.td(null, datum['2']),
                    React.DOM.td(null, datum['3']),
                    React.DOM.td(null, datum['4'])
                  )
                );
              });

              return (
                React.DOM.table(null,
                  rows
                )
              );
            }
        });
    </script>
    <script>
        angular.module('fasterAngular', []).
        controller('mycontroller', ['$scope', function($scope){
            $scope.framework = 'ReactJs';
            $scope.data = [];
            // Fill the data map with random data

            $scope.clickHandler = function(){
                console.log("in AngularJS");
            }
            $scope.refresh = function(){
                for(var i = 0; i < 1500; ++i) {
                    $scope.data[i] = {};
                    for(var j = 0; j < 5; ++j) {
                        $scope.data[i][j] = Math.random();
                    }
                }
            }
            $scope.refresh()
        }]).directive('fastRepeat', function(){
            return{
                restrict: 'E',
                scope:{
                    data: '='
                },
                link:function(scope, el, attrs){
                    scope.$watchCollection('data', function(newValue, oldValue){
                        React.renderComponent(
                            MYLIST({data:newValue}),
                            el[0]
                        );
                    })
                }
            }
        })
    </script>
</body>
</html>

To render 1500 rows, with AngularJS the time to render was about 1.35 seconds. When we let ReactJs do the rendering time was about 310ms. I attached the timeline for both from Chrome Dev Tools

 

Rendering with ReactJs, 320ms

Rendering with ReactJs, 320ms

Native AngularJs rendering 1200ms

Native AngularJs rendering 1200ms

This is just a short introduction to use ReactJs with AngularJs. For some parts of my next apps I will definitely use ReactJs to render parts of it where I think that performance will be an issue. ReactJs, as said by Pete Hunt,  is quite a game changer and the intentions behind is to show the general idea of its internal workings. I assume all major frameworks will soon have a similar concept that will be used with them or something to integrate easily. I like ReactJs and the idea behind to use a virtual DOM for the diff makes perfect sense.

In a coming up post I will write about communication between ReactJs and AngularJs, follow me on twitter to stay tuned. I am just playing around…if you think I made some terrible mistakes, ignore best practices, break stuff…etc please comment or write me and I will happily learn and update this post.

 

UPDATE 21.04.2014 21:00 CET: This post was creating some discussions and I got a bigger response than expected. Some people gave good feedback and other ideas to explore. Others questioned the usefulness of combining ReactJs with AngularJs. I try to explore the constructive feedback given and leave the more subjective response to everyone to figure it out. Find the discussion here: Hackernews,  and Reddit.

* The argument that the loaf of another JS file does not value the performance increase, did not maybe see that this performance helps increasing responsiveness of the app and therefore increase usability. And not only on initial call be on every update. For me I have been spending ours to make WebApps react faster on user input. Webapps already have a great difficulty to deal with (300ms delay). For me I will to a reasonable amount always choose what helps usability.

* The first hint was to use track by $index in the ng-repeat directive (“…With this association in place, AngularJS will not $destroy and re-create DOM nodes unnecessarily. This can have a huge performance and user experience benefit….” read here about it, and official docs) As far as I understood track by $index, this will only render faster when DOM is updated, but not on INITIAL rendering. So I tested again using the same example as above, let the test run several times and found the following results

  1. AngularJs + ReactJs: (http://plnkr.co/edit/6zfFXU?p=preview) Initial loading: ~ 243ms, Updating: ~ 125ms
  2. AngularJS using Track By (http://plnkr.co/edit/5FCsQO?p=preview) Initial loading: ~ 990ms, Updating: ~130ms
  3. AngularJs ngRepeat (http://plnkr.co/edit/YnF7Vn?p=preview) Initial loading: ~ 1100ms, Updating: 1150ms

So Track By $index does really have a huge impact when model is updated. (jn plunkrs hit refresh button in HTML), but initial rendering is still slow. I tried to run the tests several time and times may vary a little but the trend never changed. See timelines here:

AngularJs with ReactJs Updating

AngularJs with ReactJs Updating

AngularJs with ReactJs Initial Load

AngularJs with ReactJs Initial Load

Straight AngularJs. Updating

Straight AngularJs. Updating

Straight AngularJs Initial Load

Straight AngularJs Initial Load

AngularJs using Track By. Updating.

AngularJs using Track By. Updating.

AngularJs using Track By Initial Loading

AngularJs using Track By Initial Loading


* Virtual DOM in AngularJS: Ahmed pointed out in the comments (down here) the point of view of AngularJS Team on Virtual DOM.  http://www.youtube.com/watch?v=srt3OBP2kGc#t=118 As I think too, making the diff on a virtual DOM is what sooner or later will be done by default either in Browsers or frameworks. As Pete Hunt stated in the podcast linked above, that was the intentions of ReactJs’ Team

* BindOnce? I don’t think that https://github.com/Pasvaz/bindonce has anything to do here with the performance of rendering. The performance that is gained is a different one, when there are too many bindings any updates becomes slow, scrolling is lagging..etc. Here it is just writing and updating the DOM. Please comment me if you think I am wrong, or provide examples.

* Rendering using Mustache or Handlebars (In comments): I tried the plunkr and yes, rendering the DOM (initial and updating) is fast. Initial load ~ 220ms and updating is even faster ~ 120ms. That is also impressive numbers. This is even faster than with ReactJs. But I think that something goes lost here. In the examples there is always a click handler on the rows that renders to the console. Is this possible with Mustache or Handlebars? I am still working on an example to get the communication between AngularJs and ReactJs going, and I guess this is not possible with a template lib like Mustache or Handlebars. But be assured Mustache is rendering fast and I will keep it in mind for future work.

* Mithril, seems to be a new framework on the market. I know I already came across the framework once, but it seems to be rendering fast. Also using a virtual DOM. Difficult to tell which direction this framework will take? But the works seems interesting and I will check into the code to see what is so different from ReactJs virtual Dom.

Looking forward to more feedback. AngularJs 2.0 is on the way and I don’t know whtas coming, but I really appreciate the way the folks at Google have chosen to develop the framework by including the community.

Comments

  1. David Madison says

    I am not well versed in any of these Javascript frameworks, but wondered what server you are using to serve up the javascript? And, would it help to use something like the Play!Framework (a java framework) to do the meat of the work, and then use these java script frameworks to do the finishing touches?

  2. says

    Thank mate, Great article about improve rendering for angularjs.
    I wonder if I have 1000 rows, each row need a textbox to edit self value, Does the reactjs support this( so each item should support 2 way binding too ) ?

  3. Mark Jones says

    The 600px width on .site-inner makes reading the code pretty hard. I have a 1080p screen, why do you only use 600px? How many screens these days are less than 1024?

  4. says

    Hi William

    Mithril author here. Pleasantly surprised that you know about it :)

    Just to add a bit to the conversation: the reason Mithril rendering is faster than React is simply because Mithril’s internals are much simpler than React’s, despite performing an algorithmically equivalent job. Basically “to be faster, do less”

    As far as its direction goes, it aims to be a framework that can compete w/ Angular et al, but since Mithril is a lot more minimalist, a typical setup would be less monolithic, and more reliant on other open source projects for add-on functionality (e.g. accounting.js / moment.js if you care about currency and/or date formatting). I’ve seen people build up Angular-style codebases and React-style codebases on top of it, and the lower level APIs also allow you to integrate to legacy code as you have in this article, so it’s pretty flexible.

    Let me know if there’s anything else in specific that you want to know about it. Cheers!

  5. Kris says

    Hey Thierry,

    (Fellow developer from Luxembourg here ;-)

    I stumbled on your site after having Angular performance problems with repeats. Looking forward to test ReactJS. Thanks for the tip!

  6. Stefán Baxter says

    Hi,

    I’m using your approach to update table rows that get their data in through websockets (pushed to the browser 20+ per second). Using it + setTimeout(reactUpdate, <a>) to render the table body works incredibly well as this keeps them out of the Angular loop entierly. Periodically I trigger $apply() to syn the rest of the UI.

    I do realize that no Angular advocate will sanction much use of $apply() but this did the trick for me. Thank you!

    Regards,
    -Stefan

  7. Timothy A. Perez says

    I’m sure the Angular / Google team will get wind of this concept and incorporate it in future iterations of Angular (if not v2.0). I’ve seen this first hand, when it comes to heavy manipulation with multiple DOM elements. A similar concept that has had massive performance increases for me was just simply detach, manipulate, then reattach to DOM but then you may get an obvious flicker. Whereas this strategy leaves the DOM alone while achieving similar, if not better performance.

  8. Karel Fučík says

    Hi, really great post, thank you.

    I see no reason why virtual DOM should make such a big difference on initial rendering. I think it’s not in virtual DOM, it is just because React rendering is really fast.

    I’ve been developing my own framework that doesn’t use any “magic” such as virtual DOM or dirty checking, just events, getters and setters. I have recreated the example in my framework to see how it would compare – and it seems to be only about 1/4 slower than React in Chrome (probably because I use classes for models and collections instead of plain JS objects). You can see it here:

    http://karf.cz/temp/dbperf/kff.html

  9. Jorge says

    You mention:

    “The way ReactJs works is to keep a virtual DOM and render only what actually changes when the UI is updated”

    But tracking the dependencies and updating only what needs to change is what KnockouJs has been doing for years (http://knockoutjs.com/)

    I am assuming that I am missing something here… what is it?

    • says

      @Stephen
      Thanks for your comment. I updated my post and checked rendering speed with Mustache. Is really fast.
      But I think we loose the ability of binding? Example rendering data in an input field, would not be possible with Mustache, would it?

  10. says

    Thanks for the post. I think, you are right on the track,

    I would also recommend watching this http://www.youtube.com/watch?v=srt3OBP2kGc#t=118 if you haven’t done already. That is what angular team thinks on Virtual DOM.

    Also about the unnecessary bindings and $watch expressions(like in the case you render the server response and actually need to bust out, not watch for the rest of the lifespan of app), there is a great library https://github.com/Pasvaz/bindonce

    There is an active issue on bindonce to address ng-repeat issues one can encounter as you have described (unnecessary isolate scopes, in some use cases)
    https://github.com/Pasvaz/bindonce/issues/10

    • says

      @Ahmet
      Thanks for the link to the youtube part of the Angular team. I missed that video somehow.
      I know the bindonce, used it before. But in this case this does not really make a difference. I think (please correct me if wrong) the performance gained by bindonce is when there are too many bindings making the page unresponsive?

      • rnreekez says

        There is one additional scenario which would speak more directly to the work you’re doing and that’s the process of $scope.digest().

        Anything that has a watch associated with it will be processed during $scope.digest(), which occurs with every property update. By reducing the number of active watches, you’re also reducing the total time for digest which goes hand in hand with rendering time.

          • says

            @littleiffel, digest cycles exist as a result of the AngularJS’s way of handling ui updates, which is aka dirty checking. The more $watch you have, the costly a single cycle will be. So digest will not make rendering faster, it will make rendering possible. However rendering will suffer, if you have to many $watch, so the best practice is to unwatch the no longer needed $watches. Also as you know, manual $apply and $digest, most of the time are the signs of poor design.

            As of performance, I think it was not the priority of ng, the idea is to be declarative, easy to use, responsiveness < 50ms etc. Yet, now that the ideas that make ng prove themselves successful, we are expecting performance improvements.

            There is also https://github.com/swannodette/om, I haven't tried it, but it looks like a performance monster :)

Trackbacks

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre user="" computer="" escaped="">