Ember Bindings vs. Knockout.js: A Benchmark
Animating 100 (or N) circles with your standard Backbone events and Ember bindings.
by KeithPepin
HTML
<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.0.0.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.4.min.js"></script>
<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.0.0.js"></script>
<p>Folks often ask about how the difference between Knockout's and Ember's approaches to model/view binding play out in web apps. This script uses the latest Backbone.js and Ember.js to animate 500 circles (or more -- change "N" in the JavaScript), so that you can see the responsiveness after you click the button, and visualize the speed difference.</p>
<p><strong>Note: </strong> that this is just a simple benchmark: you wouldn't be doing this in an actual app.</p>
<button onclick="runEmber()">Animate with Ember</button>
<button onclick="runKnockout()">Animate with Knockout</button>
<!-- knockout -->
<div id="grid" data-bind="template: { name: 'box-template' }">
</div>
<script type='x-template' id='box-template'>
<!-- ko foreach:boxes -->
<div class="box-view" data-bind="">
<div class="box" data-bind='style: { top: topAsString(), left: leftAsString() , backgroundColor: colorAsRGB() }, text: content()'></div>
</div>
<!-- /ko -->
</script>
<script type="text/x-handlebars" id="handlebars-template" data-template-name="box">
<div class="box" {{bindAttr id="model.number" style="model.style"}}>
{{ model.content }}
</div>
</script>
CSS
p {
font: 12px/16px Arial;
margin: 10px 10px 15px;
}
button {
font: bold 14px/14px Arial;
margin-left: 10px;
}
#grid {
margin: 10px;
}
.box-view {
width: 20px; height: 20px;
float: left;
position: relative;
margin: 8px;
}
.box {
border-radius: 100px;
width: 20px; height: 10px;
padding: 5px 0;
color: #ccc;
font: 10px/10px Arial;
text-align: center;
position: absolute;
}
JavaScript
// Change N to change the number of drawn circles.
var N = 100;
// The Knockout implementation:
(function() {
var BoxViewModel = function() {
this.top = ko.observable(0);
this.left = ko.observable(0);
this.content = ko.observable(0);
this.count = ko.observable(0);
this.number = ko.observable(0);
this.color = ko.observable('');
this.colorAsRGB = ko.computed(function() {
return 'rgb(0,0,' + this.color() + ')';
}, this);
this.topAsString = ko.computed(function() {
return this.top() + 'px';
}, this);
this.leftAsString = ko.computed(function() {
return this.left() + 'px';
}, this);
this.tick = function() {
var count = this.count() + 1;
this.count(count);
this.top(Math.sin(count / 10) * 10);
this.left(Math.cos(count / 10) * 10);
this.color(count % 255);
this.content(count % 100);
};
};
var boxes;
var knockoutInit = function() {
boxes = ko.observableArray(_.map(_.range(N), function(i) {
var boxViewModel = new BoxViewModel();
boxViewModel.number(i);
return boxViewModel;
}));
ko.applyBindings({
boxes: boxes
});
};
var knockoutAnimate = function() {
for (var i = 0, l = boxes().length; i < l; i++) {
boxes()[i].tick();
}
window.timeout = _.defer(knockoutAnimate);
};
window.runKnockout = function() {
reset();
knockoutInit();
knockoutAnimate();
};
})();
// The Ember implementation:
(function() {
var Box = Ember.Object.extend({
top: 0,
left: 0,
content: 0,
count: 0,
tick: function() {
var count = this.get('count') + 1;
this.set('count', count);
this.set('top', Math.sin(count / 10) * 10);
...