Angular VS Knockout VS Ember VS React VS Mithril VS Mercury VS Ractive VS Vue VS Riot

JavaScript performance comparison

Revision 588 of this test case created by PhiLho

Preparation code

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.12/angular.min.js"></script>
<script src="http://fb.me/react-0.12.2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script src="http://builds.emberjs.com/canary/ember-template-compiler.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ember.js/1.11.1/ember.min.js"></script>
<script src="https://rawgit.com/Raynos/mercury/master/dist/mercury.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mithril/0.1.34/mithril.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.11.5/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ractive/0.7.2/ractive.js"></script>
<!--
<script src="https://cdnjs.cloudflare.com/ajax/libs/riot/2.0.14/riot.min.js"></script>
-->
<script src="https://cdn.jsdelivr.net/g/riot@2.0.14(riot.min.js+compiler.min.js)"></script>

<!-- Angular -->
<div ng-app="ngApp">
  Angular (using track by $index):
    <div ng-controller="Ctrl" id="angList"><span ng-repeat="item in data track by $index">{{item}}</span></div>
</div>

<!-- Knockout -->
<div id="koapp">
  Knockout:
  <div data-bind="foreach: data"><span data-bind="text: $data"></span></div>
</div>

<!-- Knockout Rate Limit -->
<div id="koappRL">
  Knockout (rate limit):
  <div data-bind="foreach: data"><span data-bind="text: $data"></span></div>
</div>

<!-- Ember -->
<div id="emapp">
  <script type="text/x-handlebars">
    Ember:
    <div>
      {{#each EMapp.data}}<span>{{this}}</span>{{/each}}
    </div>
  </script>
</div>

<!-- React -->
<div id="react">
  React: <div id="reactMountNode"></div>
</div>

<!-- Mithril -->
<div id="mithrilapp">
  Mithril: <div id="mithrilMountNode"></div>
</div>

<!-- Vuejs -->
<div id="vuejs">
  Vuejs: <div id="vuejsMountNode"><span v-repeat="item: data">{{ item }}</span></div></div>

<!-- Mercuryjs -->
<div id="mercuryjs">
  Mercuryjs: <div id="mercuryjsMountNode"></div>
</div>

<!-- Ractive -->
<script id='ractiveTemplate' type='text/ractive'>
  {{#each items}}<span>{{item}}</span>{{/each}}
</script>
<div id="ractive">
  Ractive: <div id="ractiveMountNode"></div>
</div>

<!-- Riot -->
<riot-component></riot-component>
<script id='riotApp' type='riot/tag'>
  <riot-component>
    Riot: <span each={ item, i in items }>{ item }</span>

    this.items = [];

    this.clear = function()
    {
      this.items.splice(0);
//      this.update();
    }
    this.push = function(e)
    {
      this.items.push(e);
//      this.update();
    }
  </riot-component>
</script>


<script>
  window.getId = function(idx)
  {
    var d = Date.now() + "";
    return idx + "-" + d.substring(d.length - 6);
  };

  var timeout = 0;
  var ngApp = angular.module('ngApp',[]);
  ngApp.controller('Ctrl', function ($scope) {
    $scope.data = [];
  });


  $(document).ready(function() {

    angular.element(document).ready(function() {
      var ang_scope = $('#angList').scope();

      window.ANGclear = function(){
        ang_scope.data = [];
      };
      window.ANGpush = function(data){
        ang_scope.data.push(data);
        if (Date.now() > timeout + 16) {
          var id = window.setTimeout(function() {
            ang_scope.$digest();
            window.clearTimeout(id);
            //console.log("triggered digest");
          }, 0);
          timeout = Date.now() + 16;
        }
      };
    });


    var KOData = ko.observableArray();
    var KOviewmodel = {data: KOData};
    ko.applyBindings(KOviewmodel, document.getElementById('koapp'));

    window.KOclear = function (){
      KOData.splice(0);
    };
    window.KOpush = function (data){
      KOData.push(data);
    };


    var KODataRL = ko.observableArray();
    KODataRL.extend({ rateLimit: 0 });
    var KOviewmodelRL = {data: KODataRL};
    ko.applyBindings(KOviewmodelRL, document.getElementById('koappRL'));

    window.KOclearRL = function (){
      KODataRL.splice(0);
    };
    window.KOpushRL = function (data){
      KODataRL.push(data);
    };


    var ENV = {EXTEND_PROTOTYPES: false};

    EMapp = Ember.Application.create({
      rootElement: '#emapp'
    });
    EMapp.data = Ember.A();

    window.EMclear = function () {
      EMapp.data.clear();
    };
    window.EMpush = function (data) {
      EMapp.data.pushObject(data);
    };


    var ReactData = [];
    var ReactUpdate = null;
    var ReactComponent = React.createClass({
      displayName: 'PerfTest',
      getInitialState: function() {
        return { data: ReactData };
      },
      componentDidMount: function() {
        ReactUpdate = this._onData;
      },
      render: function() {
        return React.DOM.span(null,
            this.state.data.map(function(result) {
              return React.DOM.span(null, result);
            })
          );
      },
      _onData: function() {
        this.setState({ data: ReactData });
      }
    });
    var reactComp = ReactComponent();
    React.renderComponent(reactComp, document.getElementById('reactMountNode'));


    window.RClear = function() {
      ReactData.splice(0);
      ReactUpdate();
    };

    window.RPush = function(data) {
      ReactData.push(data);
      ReactUpdate();
    }


    var h = mercury.h
    var mdata = mercury.array([])
    mercury.app(document.getElementById('mercuryjsMountNode'), mdata, mrender)
    function mrender(data) {
      return h("span", data.map(function(datum) { return h('span', datum)}));
    }

    window.mercuryClear = function(){
      mdata.splice(0)
    }

    window.mercuryPush = function(data){
      mdata.push(data)
    }


    var MithrilData = [];
    var mithapp = {
      controller: function() {
      },
      view: function(ctrl) {
        return m("span", [MithrilData.map(function(datum) { return m('span', datum); })]);
      }
    }
    m.module(document.getElementById("mithrilMountNode"), mithapp);

    window.Mclear = function() {
      MithrilData = [];
    };
    window.Mpush = function(data) {
      MithrilData.push(data);
      if (Date.now() > timeout + 16) {
          var id = window.setTimeout(function() {
          m.startComputation();
          m.endComputation();
          window.clearTimeout(id);
        }, 0);

        timeout = Date.now();
      }
    }


    var vueInstance = new Vue({
      el: '#vuejsMountNode',
      data: {
        data: []
      }
    });

    window.VueClear = function(){
      vueInstance.data = []
    };
    window.VuePush = function(data){
      vueInstance.data.push(data);
    };


    Ractive.DEBUG = false;
    var RactiveData = [];
    var RactiveComponent = new Ractive({
      el: '#ractiveMountNode',
      template: '#ractiveTemplate',
      data: { items: RactiveData }
    });

    window.RactiveClear = function() {
      RactiveData.splice(0, RactiveData.length);
    };
    window.RactivePush = function(data) {
      // I don't know how to use plain strings in array in a #each loop
      RactiveData.push({ item: data });
    };


    var riotComponent = riot.mount('riot-component')[0];

    window.RiotClear = function()
    {
      riotComponent.clear();
    }
    window.RiotPush = function(data)
    {
      riotComponent.push(data);
      if (Date.now() > timeout + 16) {
        var id = window.setTimeout(function() {
          riotComponent.update();
          window.clearTimeout(id);
        }, 0);
        timeout = Date.now() + 16;
      }
    }

  }); // document.ready

</script>

    

Preparation code output

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.12/angular.min.js"></script> <script src="http://fb.me/react-0.12.2.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script> <script src="http://builds.emberjs.com/canary/ember-template-compiler.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ember.js/1.11.1/ember.min.js"></script> <script src="https://rawgit.com/Raynos/mercury/master/dist/mercury.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/mithril/0.1.34/mithril.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.11.5/vue.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ractive/0.7.2/ractive.js"></script> <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/2.0.14/riot.min.js"></script> --> <script src="https://cdn.jsdelivr.net/g/riot@2.0.14(riot.min.js+compiler.min.js)"></script> <!-- Angular --> <div ng-app="ngApp"> Angular (using track by $index): <div ng-controller="Ctrl" id="angList"><span ng-repeat="item in data track by $index">{{item}}</span></div> </div> <!-- Knockout --> <div id="koapp"> Knockout: <div data-bind="foreach: data"><span data-bind="text: $data"></span></div> </div> <!-- Knockout Rate Limit --> <div id="koappRL"> Knockout (rate limit): <div data-bind="foreach: data"><span data-bind="text: $data"></span></div> </div> <!-- Ember --> <div id="emapp"> <script type="text/x-handlebars"> Ember: <div> {{#each EMapp.data}}<span>{{this}}</span>{{/each}} </div> </script> </div> <!-- React --> <div id="react"> React: <div id="reactMountNode"></div> </div> <!-- Mithril --> <div id="mithrilapp"> Mithril: <div id="mithrilMountNode"></div> </div> <!-- Vuejs --> <div id="vuejs"> Vuejs: <div id="vuejsMountNode"><span v-repeat="item: data">{{ item }}</span></div></div> <!-- Mercuryjs --> <div id="mercuryjs"> Mercuryjs: <div id="mercuryjsMountNode"></div> </div> <!-- Ractive --> <script id='ractiveTemplate' type='text/ractive'> {{#each items}}<span>{{item}}</span>{{/each}} </script> <div id="ractive"> Ractive: <div id="ractiveMountNode"></div> </div> <!-- Riot --> <riot-component></riot-component> <script id='riotApp' type='riot/tag'> <riot-component> Riot: <span each={ item, i in items }>{ item }</span> this.items = []; this.clear = function() { this.items.splice(0); // this.update(); } this.push = function(e) { this.items.push(e); // this.update(); } </riot-component> </script> <script> window.getId = function(idx) { var d = Date.now() + ""; return idx + "-" + d.substring(d.length - 6); }; var timeout = 0; var ngApp = angular.module('ngApp',[]); ngApp.controller('Ctrl', function ($scope) { $scope.data = []; }); $(document).ready(function() { angular.element(document).ready(function() { var ang_scope = $('#angList').scope(); window.ANGclear = function(){ ang_scope.data = []; }; window.ANGpush = function(data){ ang_scope.data.push(data); if (Date.now() > timeout + 16) { var id = window.setTimeout(function() { ang_scope.$digest(); window.clearTimeout(id); //console.log("triggered digest"); }, 0); timeout = Date.now() + 16; } }; }); var KOData = ko.observableArray(); var KOviewmodel = {data: KOData}; ko.applyBindings(KOviewmodel, document.getElementById('koapp')); window.KOclear = function (){ KOData.splice(0); }; window.KOpush = function (data){ KOData.push(data); }; var KODataRL = ko.observableArray(); KODataRL.extend({ rateLimit: 0 }); var KOviewmodelRL = {data: KODataRL}; ko.applyBindings(KOviewmodelRL, document.getElementById('koappRL')); window.KOclearRL = function (){ KODataRL.splice(0); }; window.KOpushRL = function (data){ KODataRL.push(data); }; var ENV = {EXTEND_PROTOTYPES: false}; EMapp = Ember.Application.create({ rootElement: '#emapp' }); EMapp.data = Ember.A(); window.EMclear = function () { EMapp.data.clear(); }; window.EMpush = function (data) { EMapp.data.pushObject(data); }; var ReactData = []; var ReactUpdate = null; var ReactComponent = React.createClass({ displayName: 'PerfTest', getInitialState: function() { return { data: ReactData }; }, componentDidMount: function() { ReactUpdate = this._onData; }, render: function() { return React.DOM.span(null, this.state.data.map(function(result) { return React.DOM.span(null, result); }) ); }, _onData: function() { this.setState({ data: ReactData }); } }); var reactComp = ReactComponent(); React.renderComponent(reactComp, document.getElementById('reactMountNode')); window.RClear = function() { ReactData.splice(0); ReactUpdate(); }; window.RPush = function(data) { ReactData.push(data); ReactUpdate(); } var h = mercury.h var mdata = mercury.array([]) mercury.app(document.getElementById('mercuryjsMountNode'), mdata, mrender) function mrender(data) { return h("span", data.map(function(datum) { return h('span', datum)})); } window.mercuryClear = function(){ mdata.splice(0) } window.mercuryPush = function(data){ mdata.push(data) } var MithrilData = []; var mithapp = { controller: function() { }, view: function(ctrl) { return m("span", [MithrilData.map(function(datum) { return m('span', datum); })]); } } m.module(document.getElementById("mithrilMountNode"), mithapp); window.Mclear = function() { MithrilData = []; }; window.Mpush = function(data) { MithrilData.push(data); if (Date.now() > timeout + 16) { var id = window.setTimeout(function() { m.startComputation(); m.endComputation(); window.clearTimeout(id); }, 0); timeout = Date.now(); } } var vueInstance = new Vue({ el: '#vuejsMountNode', data: { data: [] } }); window.VueClear = function(){ vueInstance.data = [] }; window.VuePush = function(data){ vueInstance.data.push(data); }; Ractive.DEBUG = false; var RactiveData = []; var RactiveComponent = new Ractive({ el: '#ractiveMountNode', template: '#ractiveTemplate', data: { items: RactiveData } }); window.RactiveClear = function() { RactiveData.splice(0, RactiveData.length); }; window.RactivePush = function(data) { // I don't know how to use plain strings in array in a #each loop RactiveData.push({ item: data }); }; var riotComponent = riot.mount('riot-component')[0]; window.RiotClear = function() { riotComponent.clear(); } window.RiotPush = function(data) { riotComponent.push(data); if (Date.now() > timeout + 16) { var id = window.setTimeout(function() { riotComponent.update(); window.clearTimeout(id); }, 0); timeout = Date.now() + 16; } } }); // document.ready </script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Angular
ANGclear();
for (var i = 0; i < 100; i++)
{
  ANGpush("nitem" + getId(i));
}
pending…
Knockout
KOclear();
for (var i = 0; i < 100; i++)
{
  KOpush("kitem" + getId(i));
}


pending…
React
RClear();
for (var i = 0; i < 100; i++)
{
  RPush("ritem" + getId(i));
}

pending…
Mithril
Mclear();
for (var i = 0; i < 100; i++)
{
  Mpush("mitem" + getId(i));
}

pending…
vuejs
VueClear();
for (var i = 0; i < 100; i++)
{
  VuePush("vitem" + getId(i));
}
pending…
Ember
EMclear();
for (var i = 0; i < 100; i++)
{
  EMpush("eitem" + getId(i));
}

pending…
Mercury
mercuryClear();
for (var i = 0; i < 100; i++)
{
  mercuryPush("mitem" + getId(i));
}

pending…
Ractive
RactiveClear();
for (var i = 0; i < 100; i++)
{
  RactivePush("aitem" + getId(i));
}

pending…
Knockout Rate Limit
KOclearRL();
for (var i = 0; i < 100; i++)
{
  KOpushRL("kitem" + getId(i));
}

pending…
Riot
RiotClear();
for (var i = 0; i < 100; i++)
{
  RiotPush("ritem" + getId(i));
}
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments

Angular (using track by $index):
{{item}}
Knockout:
Knockout (rate limit):
React:
Mithril:
Vuejs:
{{ item }}
Mercuryjs:
Ractive: