A Circle Benchmark: Backbone vs. Ember vs. React vs. Ractive vs. Vue vs. Basis.js

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
<script src="https://rawgithub.com/ebryn/0bbe6639da1166bef3ee/raw/c5f715bbee4200d328065fdb34b0bb49f2d748cf/ember-htmlbars-dont-use-me-anywhere-else.prod.js"></script>
<script src="http://react.zpao.com/builds/master/latest/react.min.js"></script>
<script src="https://cdn.rawgit.com/vuejs/vue/50cf2ab528e2c272eb852f96ebb00f220a6371ba/dist/vue.js"></script>
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/basis.js/1.2.1/basis.min.js"></script>
<script src="https://cdn.rawgit.com/Raynos/mercury/6e260f4cde227683552fd9ca8c3c242be8d5b07c/dist/mercury.js"></script>

<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-alpha.44/angular2.dev.js"></script>
<script>
    System.config({
      transpiler: 'typescript',
      typescriptOptions: {
        emitDecoratorMetadata: true
      },
      map: {
        app: "https://rawgit.com/cheapsteak/ef4fea6105469ea34a4e/raw/09dbf65e59b3d74fd7343632486f8254f603691a"
      },
      packages: {
        app: {
          main: './test-angular.ts',
          defaultExtension: 'ts'
        }
      }
    });
    System.import('app')
      .catch(console.error.bind(console));
</script>

<p>
    This is a fork of <a href="http://jsfiddle.net/jashkenas/CGSd5/" target="_top">jashkenas's Ember/Backbone</a> benchmark to include Vue.js. Note that this is just a simple benchmark: you wouldn't be doing this in an actual app.
</p>

<p>The original fork was clocking the loops synchronously which was not accurate. This fork fixes that, and also gives a 300ms pause before clock starts to offset initialization...

CSS

p {
  font: 12px/16px Arial;
  margin: 10px 10px 15px;    
}

button {
  font: bold 14px/14px Arial;  
  margin-left: 10px;
  padding: 3px;
  margin-top: .5em;
}

#grid {
  margin: 10px;
}

#timing {
  clear: both;
  padding-top: 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: #fff;
  font: 10px/10px Arial;
  text-align: center;
  position: absolute;
}

JavaScript

// Change N to change the number of drawn circles.

window.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 EfficientBoxView = Backbone.View.extend({

    className: 'box-view',

    template: _.template($('#underscore-template').html()),

    styleTemplate: _.template("top: <%= top %>px; left: <%= left %>px; background: rgb(0,0,<%= color %>);"),

    boxEl: undefined,

    initialize: function() {
        this.model.bind('change', this.update, this);
    },

    render: function() {
        this.$el.html(this.template(this.model.attributes));
        this.boxEl = this.el.querySelector('.box');
        return this;
    },

    update: function () {
        this.boxEl.setAttribute('style', this.styleTemplate(this.model.attributes));
        this.boxEl.innerHTML = this.model.attributes.content;
    }

});

var boxes;

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

var backboneAnimate = function() {
    for...