CanJS and Backbone both provide structure for JavaScript applications through an MVC-like pattern.
CanJS is a lightweight MVC framework built to provide structure for JavaScript applications. It is aimed at developers of any library who need a battle-hardened library that makes building complex applications easy. It is part of a larger offering named JavaScriptMVC from Bitovi (its creators) that has documentation, testing and dependency management tools.
Backbone is a flexible, minimalist solution for seperating concerns in a JavaScript application. It provides just a little bit of structure to a jQuery application and has largely attracted developers who know just enough jQuery to get by and now want to organize their code a bit more.
CanJS was originally a project named jQueryMX which provided the MVC functionality for JavaScriptMVC which is 5 years old. CanJS/jQueryMX has been used in many large, complex applications in its 5 years:
- Norton eCommerce
- MindJet
- FrogOS
- Apple Online Store
- Bootswatchr
- Cengage Mindtap
Backbone has been around for just over two years and in that time has been used in many applications/websites:
- DocumentCloud
- USAToday
- Rdio
- LinkedIn Mobile
- Disqus
- Khan Academy
One caveat is that Backbone is often used on brochure-type sites that would normally just use jQuery and are not actually applications.
Verdict: Draw
The technology CanJS is built on is over 5 years old. CanJS has been at version 1.x for almost a year.
Backbone is at version 0.9.x which usually implies that the API could drastically change before they hit a 1.0 release.
Verdict: CanJS
CanJS releases a new version about every 6 weeks.
Official Backbone updates are few and far between. Plugins for Backbone and unofficial fixes are used as stop gaps. For example it jumped from 0.5.3 in August 2011 to 0.9.0 in January 2012 with no updates in between (5 months). Then from 0.9.2 in March 2012 to 0.9.9 in December 2012 (9months).
Verdict: CanJS
CanJS has an overview site at canjs.us with many examples and detailed API docs at donejs.com. Annotated source is also available.
Backbone has an overview/API site at Backbonejs.org and annotated source code. It has many more examples due to its popularity.
Verdict: Backbone
CanJS is supported full-time by Bitovi, a JavaScript consultancy and a small, active community.
Backbone has a very large community.
Verdict: Draw
Gzipped and compressed CanJS is ~11KB
Gzipped and compressed Backbone is ~9KB with Underscore.
By comparison, Ember is 37KB.
Verdict: Backbone
However, Backbone comes as an all in one download. CanJS has a download builder where you pick just the comoponents you want, and it compiles them into a single minified script. Using this tool you can customize CanJS to not include components you aren't using, which isn't possible in Backbone.
CanJS supports EJS and Mustache templates out of the box It also has built in support to integrate these templates with Steal's build system, making moving to production very simple and brainless Mustache and EJS have built in live binding support in Can Can also adds convenient view features that are not possible in Backbone, like handling deferreds that are pass into a view:
can.view( 'todos/todos.ejs', {
todos: Todo.findAll(),
user: User.findOne( { id: 5 } )
} ).then(function( frag ){
document.getElementById( 'todos' )
.appendChild( frag );
})
Backbone lacks any built in templating engine. You have to integrate your own, and you only get very basic template rendering Its also up to you to figure out how to build templates into a production file for better performance in production (otherwise each file will load individually, which is very bad).
Verdict: CanJS
Initializing Controls performs better in CanJS than Backbone: http://jsperf.com/tabs-timing-test/7
This is because when creating controls, CanJS caches several expensive calculations, so subsequent controls initialize very fast. This is important for complex apps with a lot of similar controls on the page.
Changes in templates appear much faster in CanJS than Backbone: http://jsperf.com/canjs-ejs-performance/5
This is because CanJS has true live binding and when one data attribute changes, it makes the smallest change possible to update that attribute's representation in the UI (ie changing a single element's attribute). Backbone must re-render the entire view for every change.
Verdict: CanJs
CanJS uses some clever techniques that virtually eliminate any possibility of having memory leaks. These are well documented here.
Its common while creating controls that you listen to events outside the control's element
. For example, a menu control needs to listen to window click to know when to close itself. When the menu is destroyed, all event handlers that exist inside the control's element
are unbound. But any event handlers that live outside (like the window click handler) are not unbound.
This creates a huge problem in long running large JS apps. Each un-cleaned up event handler has a closure that could reference an element, which means even that element can't be garbage collected:
// inside Backbone.View Tooltip code
$(window).bind('click', function(){
// this element won't be removed from memory after the tooltip is gone
if (!this.element.has( ev.target ) {
this.element.remove();
}
});
In Backbone, there is no solution for this. Memory leaks are common and will slow down your app and cause unexpected errors. In CanJS you can use templated event handlers to bind events to elements outside a control's element
and they will all be automatically unbound when the control's element
is unbound:
var Tooltip = can.Control({
'{window} click': function( el, ev ) {
// hide only if we clicked outside the tooltip
if (!this.element.has( ev.target ) {
this.element.remove();
}
}
})
Another leaky problem is related to large sets of data stored in JS. Big JS apps often grab large data sets from the server. If they are long-lived, sometimes that data becomes stale and is replaced by new data. If the data is kept in a store in memory, the app will quickly consume a lot of local memory, even though many of these objects in the store are no longer needed.
CanJS has a model store that cleans up model instances that are no longer bound to anything. This also allows you to represent the same model in multiple places in your UI and have changes to that model properly reflected everywhere. Even though a model may be in the UI two or three times, only a single instance is created.
In CanJS every model data object has a _bindings property that keeps track of how many times something has bound to a property change for this element. Any time this object is bound to, this property is incremented, and when it is unbound, it is decremented (including for live binding in a template). When this count goes down to 0, can.Model deletes this data from the store, leaving you with a lighter memory footprint.
Backbone does nothing to try to solve this problem and often you are left with "zombie views" which are views that are not being used (dead), but still linger.
Verdict: CanJS
CanJS works with any major DOM library: YUI, Dojo, Zepto, jQuery, or Mootools. This is nice when working in an organization where more than one DOM lib is being used. There is only one application API to learn.
Backbone works with underscore and jQuery or Zepto.
Verdict: CanJS
Using CanJS, you only have to worry about how you represent data once, while creating your view.
Backbone has no concept of binding data object and their changes to your templates, so every single time data changes, you have to either a) manually trigger an event that re-renders entire views with the new data or b) write some "glue" code that specifically changes a small part of the DOM when the data changes like this:
'quantity:change' : function() {
var newPrice = this.model.attributes.count * this.model.attributes.price;
this.$el.find('div.results').html(newPrice);
}
Backbone provides a basic event system but fails to make this part of the view layer. This means that even when only one model attribute changes, large parts or all of the view template need to be re-rendered or the update needs to be handled manually.
Considering the following template:
<div id="cart">
You have {{items.length}} items in your cart:
<ul>
{{#items}}
<li>{{count}} {{name}} {{price}} <strong>{{total}}</strong></li>
{{/items}}
</ul>
{{#if discount}}
<p>Discount: <strong>{{discount}}</strong></p>
{{/if}}
Total: <strong>{{cart_total}}</strong>
</div>
In Backbone, whenever any of the the attributes changes (single item count, cart total etc.) the entire template needs to be rendered again or the specific attribute selected with jQuery and updated manually from the model information.
Both cases are less efficient than CanJS live-binding which only updates the attribute that actually changed. After the template has been rendered for the first time, there is no need to take care of updating the view anymore. Working directly with the data lets you focus on what you actually want to do:
// Remove the last item. Removes the item and updates the cart total and item count.
this.cart.attr('items').pop();
// Update the count of the second item. Also updates the item and cart total automatically
this.cart.attr('items.1.count', 2);
// Add a discount, will show the discount section and update cart total
this.cart.attr('discount', 25.53);
Verdict: CanJs
Frameworks come and go all the time. GWT made a lot of noise a few years ago and Google silently stopped working on it and has all but killed the project. Google could do the same with Angular at any time. Not that they will, but they don't have much of a vested need for it in the same way that Bitovi's business model depends on the success of CanJS.
This framework will be actively developed as long as Bitovi exists, and we have plans to exist for a while. DocumentCloud created Backbone, but their business is not Backbone development or consulting.
Verdict: CanJS
Sometimes its necessary to create features that extend the core functionality of the library. Backbone and CanJS are pretty even in this category. Both try to expose methods that might be useful hooks for additional functionality.
For example, in Can, you can create a validations plugin by overwriting can.Observe.prototype.__set The annotated source for this plugin is here.
The source of CanJS is written to be as readable and modular as possible, just like the apps built from it Annotated source is here.
Verdict: Draw
You can hire the developers that wrote CanJS (us) We will answer questions instantly, fix bugs immediately, and even add features that your app needs.
Your best bet for Backbone support is to hire a consultant who has used the framework, but the developers who made it aren't for hire. for support questions you would have to have to ask in a forum or IRC and hope for the best.
Verdict: CanJS
The reason people love Backbone and the main reason its so popular is because its very easy for a beginner to pick it up By contrast Ember is notoriously difficult to learn and understand because its very different from vanilla jQuery development.
CanJS is VERY similar to Backbone on first glance Controls organize event handlers, views render templates, models organize AJAX calls, and its all synchronous, unlike Ember, which has a confusing and difficult to debug asycnhronous event loop.
Verdict: Draw