Faster AngularJS Rendering (AngularJS and ReactJS)

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  orhere. 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 previouspost, 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.

Reference:

http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/

https://www.quora.com/profile/Pete-Hunt/Posts/Facebooks-React-vs-AngularJS-A-Closer-Look