Angular vs Knockout vs Ember vs Ractive vs Ant

Benchmark created on


Description

Angular.js, Knockout.js, Ractive.js, Ant.js 的一个性能测试.

Preparation HTML

<style type="text/css">
.box-view {
  width: 20px; height: 20px;
  float: left;
  position: relative;
  margin: 8px;    
}

.box {
  border-radius: 100px;
  width: 20px; height: 10px;
  padding: 5px 0;
  color: #fff;
  font: 10px/10px Arial;
  text-align: center;
  position: absolute;
}   
</style>
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>

<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script>

<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>

<script src="https://rawgithub.com/antjs/ant.js/v0.2.2/dist/ant.min.js"></script>

<script src="https://rawgithub.com/RactiveJS/Ractive/master/release/0.3.7/Ractive.min.js"></script>



<div id="bb-box"></div>
<script type="x-template" id="underscore-template">
  <div class="box" id="box-<%= number %>" style="top: <%= top %>px; left: <%= left %>px; background: rgb(0,0,<%= color %>);">
    <%= content %>       
  </div>
</script>
        
<div id="ag-box" ng-app ng:controller="animationCtrl">
    <div class="box-view" ng:repeat="box in boxes">
        <div class="box" ng:style="{top: box.top+'px', left: box.left+'px', background: 'rgb(0,0,'+box.color+')'}">{{box.content}}</div>
    </div>
</div>
        

<div id="ko-box">
    <div data-bind="foreach: boxes">
        <div class="box-view">
            <div class="box" data-bind="style: {top: top(), left: left(), background: color()}, text: content()"></div>
        </div>
    </div>
</div>
    
<div id='ra-box'></div>
<script type="text/x-handlebars" id="ractive-template">
{{#boxes}}
  <div class="box-view">
      <div class="box" style='top: {{top}}px; left: {{left}}px;background-color: rgb(0, 0, {{color}})'>{{content}}</div>
  </div>
{{/boxes}}
</script>


<div id='ant-box'>
  <div class="box-view" a-repeat="{{boxes}}">
      <div class="box" a-style='top: {{top}}px; left: {{left}}px;background-color: rgb(0, 0, {{color}})'>{{content}}</div>
  </div>
</div>


<script type="text/javascript">
// Change N to change the number of drawn circles.

var N = 100;

// The Backbone implementation:
(function(){
    
var Box = Backbone.Model.extend({

    defaults: {
        top: 0,
        left: 0,
        color: 0,
        content: 0
    },
    
    initialize: function() {
        this.count = 0;
    },

    tick: function() {
        var count = this.count += 1;
        this.set({
            top: Math.sin(count / 10) * 10,
            left: Math.cos(count / 10) * 10,
            color: (count) % 255,
            content: count % 100
        });
    }        

});


var BoxView = Backbone.View.extend({
    
    className: 'box-view',
    
    template: _.template($('#underscore-template').html()),
   
    initialize: function() {
        this.model.bind('change', this.render, this);
    },
    
    render: function() {
        this.$el.html(this.template(this.model.attributes));
        return this;
    }
    
});

var boxes;

var backboneInit = function() {
    boxes = _.map(_.range(N), function(i) {
        var box = new Box({number: i});
        var view = new BoxView({model: box});
        $('#bb-box').append(view.render().el);
        return box;
    });
};

var backboneAnimate = function() {
    for (var i = 0, l = boxes.length; i < l; i++) {
      boxes[i].tick();   
    }
};

  backboneInit();
  
window.runBackbone = function() {
  backboneAnimate();    
};

})();

// The angular implementation:
function animationCtrl($scope, $window, $rootScope) {
    var self = this;
    var start, counter = 0;
    $scope.count = 0;
    $scope.boxes = new Array();

    $scope.Box = function(n) {
        this.number = n;
        this.top = 0;
        this.left = 0;
        this.color = 0;
        this.content = 0;
        this.count = 0;

        this.tick = function() {
            var count = this.count += 1;
            this.top = Math.sin(count / 10) * 10;
            this.left = Math.cos(count / 10) * 10;
            this.color = count % 255;
            this.content = count % 100;
        }
    }

    $scope.angularjsInit = function() {
        for (var i = 0; i < N; i++) {
            $scope.boxes[i] = new $scope.Box(i);
        }
    }

    $scope.angularjsAnimate = function() {
        if (counter == 0) start = new Date().getTime();
        counter += 1;
        for (var i = 0; i < N; i++) {
            $scope.boxes[i].tick();
        }
        $rootScope.$apply();
    }
        
    $scope.angularjsInit();
        
    window.runAngularjs = function() {
        $scope.angularjsAnimate();
    }
}

// The Knockout implementation:
(function(){

var Box = function() {
    var that = this;
    that.count = ko.observable(0);
    that.top = ko.computed(function(){ return (Math.sin(that.count() / 10) * 10) + 'px'});
    that.left = ko.computed(function(){ return (Math.cos(that.count() / 10) * 10) + 'px'});
    that.color = ko.computed(function() { return 'rgb(0,0,' + (that.count()) % 255 + ')'});
    that.content = ko.computed(function() { return that.count() % 100});
    
    this.tick = function() {
        that.count(that.count() + 1);
    }
}
    

var ViewModel = function(num) {
    this.num = num;
    this.boxes = ko.observableArray();
    for(var i = 0; i < num; i++) {
        this.boxes.push(new Box())
    }
}

var knockoutAnimate = function() {
        for (var i = 0, l = vm.boxes().length; i < l; i++) {
            vm.boxes()[i].tick();
        }
    }

var vm = new ViewModel(N)
ko.applyBindings(vm, $('#ko-box')[0]);
  
window.runKnockout = function() {
  knockoutAnimate();
};
    
})();

// The Ractive implementation:
(function(){
    var boxes = [];
    for(var i = 0; i < N; i++){
        boxes.push({
            color: 0,
            top: 0,
            left: 0,
            content: 0
        });
    }
    var Box = Ractive.extend({
        tick: function(){
            var count = this.count = this.count+1;
            for(var i = 0; i < N; i++){
                this.set('boxes.' + i, {
                    top: Math.sin(count / 10) * 10,
                    left: Math.cos(count / 10) * 10,
                    color: count % 255,
                    content: count % 100
                });
            }
        },
        init: function() {
            this.count = 0;
        }
    });
    
    var box = new Box({
      el: 'ra-box',
      template: $('#ractive-template').html(),
      data: {boxes: boxes}
    });
    
    window.runRactive = function() {
      box.tick();
    }
})();


// The Ant.js implementation:
(function(){
    var boxes = [];
    for(var i = 0; i < N; i++){
        boxes.push({
            color: 0,
            top: 0,
            left: 0,
            content: 0
        });
    }
    var Box = Ant.extend({
        tick: function(){
            var count = this.count = this.count+1;
            for(var i = 0; i < N; i++){
                this.set('boxes.' + i, {
                    top: Math.sin(count / 10) * 10,
                    left: Math.cos(count / 10) * 10,
                    color: count % 255,
                    content: count % 100
                });
            }
        },
        init: function() {
            this.count = 0;
        }
    });
    
    
    var box = new Box($('#ant-box')[0], {
        data: {boxes: boxes}
    });
    
    window.runAnt = function() {
      box.tick();
    }
})();

</script>

Test runner

Ready to run.

Testing in
TestOps/sec
backbone
runBackbone()
ready
angular
runAngularjs()
ready
knockout
runKnockout()
ready
ractive
runRactive();
ready
ant.js
runAnt()
ready

Revisions

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