Este pasado sábado 26 de febrero, tuvimos el Four Sessions de Techdencias, en el que me lo pasé genial participando junto a Marc Rubiño en un duelo entre Knockout y Backbone para ver que librería era “mejor” para construir aplicaciones web. Al final la gente votó y la verdad és que ganó Marc por KO… 😛
Como digo estuvo divertido, aunque comparar Backbone con Knockout es como comparar peras con guisantes ya que, realmente, poco tenen que ver y además son muy complementarias. Backbone se centra más en dotarnos de herramientas para poder arquitecturar nuestra aplicación javascript. Mientras que Knockout tiene un sistema fenomenal de data-binding entre el DOM y el viewmodel pero nada más que eso (que no es poco): sigue siendo tarea nuestra organizar bien nuestra aplicación javascript y de ello Knockout se mantiene al margen.
La consecuencia de estos dos enfoques es que el código javascript que tiene que colocarse en Backone es siempre muy superior al de Knockout. No sólo eso, si no que Backbone por su propia concepción requiere unas habilidades de javascript superiores a las que requiere Knockout para empezar a hacer cosas. Una vez superados estos dos escollos, Backbone ofrece más facilidades que Knockout no sólo para organizar nuestro código de cliente, si no también para validaciones de modelos y creación de aplicaciones SPA.
Dos modelos distintos pero similares
Knockout apuesta por un modelo MVVM, muy semejante al que se encuentra en las tecnologías basadas en XAML: los enlaces se aplican declarativamente en el DOM y en javascript basta con crear un viewmodel que contenga los datos que son enlazados automáticamente (y de forma bidireccional). Knockout ofrece soporte para varios tipos de bindings y la capacidad de definirnos nuestros propios para tareas más avanzadas.
El siguiente código muestra un textbox y una label que se va modificando a medida que vas modificando el contenido del textbox:
<!DOCTYPE html>
<html>
<head>
<script src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
</head>
<body>
<input type="text" data-bind="value: name, valueUpdate: ‘afterkeydown’" />
<br />
<label data-bind="text: greeting" />
</body>
<script>
(function () {
var MyViewModel = function (aName) {
var self = this;
self.name = ko.observable(aName);
self.greeting = ko.computed(function () {
return "Hola " + self.name();
});
}
var vm = new code("eiximenis");
ko.applyBindings(vm);
})();
</script>
</html>
Podemos ver el uso del atributo data-bind para declarar los enlaces declarativamente y el código javascript que se limit a crear el viewmodel con las dos propiedades usadas (name y greeting).
Backbone por su parte apuesta por un modelo más parecido a MVC, con sus modelos y sus vistas claramente diferenciados. Para crear la misma aplicación con Backbone se requiere bastante más código:
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
<script type="text/template" id="viewTemplate">
<input type="text" value="<%- name %>" />
<br />
<label><%- greeting %></label>
</script>
</head>
<body>
</body>
<script>
(function () {
var MyModel = Backbone.Model.extend({
defaults: { name: ‘eiximenis’ },
});
var MyView = Backbone.View.extend({
initialize: function () {
var self = this;
this.model.on(‘change:name’, function () {
self.updateLabel();
});
},
events: { ‘keyup input’: ‘changeName’ },
changeName: function (evt) {
this.model.set(‘name’, $(evt.target).val());
},
updateLabel: function () {
this.$("label").text(this.greeting());
},
greeting: function () {
return "Hola " + this.model.get(‘name’);
},
render: function () {
this.$el.empty();
var data = { name: this.model.get(‘name’), greeting: this.greeting() };
this.$el.html(_.template($("#viewTemplate").html(), data));
return this;
}
});
$(function () {
var model = new MyModel();
var view = new MyView({ model: model, el: ‘body’ });
view.render();
});
})();
</script>
</html>
Aunque pueda parecer que están a las antípodas, en el fondo los modelos de Knockout y Backbone son similares: ambos tienen una clase javascript que se encarga de mantener los datos y ambos tienen una vista que se encarga de representarlos. La diferencia fundamental es que en Knockout la vista es el propio DOM que contiene los atributos data-bind y en Backbone la vista debe crearse y debe tener todo el código necesario para gestionar la comunicación entre el DOM (DOM que crea la propia vista) y el modelo de datos.
¿Y pueden trabajar juntos?
La verdad es que para pequeñas aplicaciones no tiene sentido usar Backbone, y probablemente con Knockout tirarás perfectamente. Pero, si nos ponemos en la situación de que queremos usar Backbone… no podríamos aprovecharnos del sistema de enlaces de Knockout y ahorrarnos toda esa parte?
Pues sí, y la respuesta se llama Knockback.
Usando Knockback nuestro código quedaría más o menos como:
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script src="https://raw.github.com/kmalakoff/knockback/0.16.8/knockback.min.js"> </script>
</head>
<body>
<input type="text" data-bind="value: name, valueUpdate:’afterkeydown’" />
<br />
<label data-bind="text: greeting"></label>
<input type="button" id="ju" value="!!" />
</body>
<script>
(function () {
var MyModel = Backbone.Model.extend({
defaults: { name: ‘eiximenis’ },
});
$(function () {
var model = new MyModel();
var ViewModel = function (model) {
var self = this;
this.name = kb.observable(model, ‘name’);
this.greeting = ko.computed(function () {
return "Hello " + self.name();
})
}
ko.applyBindings(vm);
});
})();
</script>
</html>
En el fondo volvemos a un esquema muy parecido al código con knockout, la única diferencia principal es que ahora tenemos un modelo de Backbone y un viewmodel de Knockout. Lo que ya no tenemos (puesto que nos lo proporciona Knockout) es la vista de Backbone.
Evidentemente, si en este ejemplo no tiene mucho sentido aplicar Backbone, tampoco lo tendrá aplicar Knockback, pero vamos a hacer un ejercicio de imaginación y situarnos en un proyecto mucho más complejo.
En este caso, al usar Knockback obtenemos la sencillez de Knockout para los enlaces y además la potencia de Backbone para los modelos. En nuestro ejemplo ViewModel y modelo eran casi idénticos, pero en un ejemplo más grande el ViewModel puede diferir bastante del modelo: Este contiene los datos tal y como van a ser enviados y recibidos desde el servidor y lo gestiona Backbone. Así nos podemos aprovechar de la persistencia automática de Backbone, el enrutamiento en cliente, la ordenación y filtrado de colecciones automática y todo lo que Backbone nos ofrece en los modelos. Por otra parte el Viewmodel contendría los datos preparados para ser enlazados directamente con el DOM.
Imagina una aplicación que muestre los datos generales de una póliza, y luego tenga varias pestañas con información addicional. Pues bien, toda la información puede estar en un modelo de Backbone, que contendrá todos los datos de la póliza, mientras que tendríamos varios viewmodels de Knockout, uno por cada pestaña, que son los que usaríamos para enlazar con el DOM.
Lo que Knockback nos ofrece es que los viewmodels y los modelos estén sincronizados entre ellos (si modifico algo en el viewmodel el modelo se entera de dicha modifcación).
A modo de resumen podríamos decir que un Modelo de Backbone:
- Es independiente del DOM
- Ligado fuertemente a los datos que vienen del servidor
- Representa datos complejos, muchos de los cuales pueden no visualizarse
- Se modifica a través de la lógica de negocio
Mientras que un ViewModel de Knockout:
- Está atado a la vista, en el sentido que contiene aquellas propiedades que la vista quiere mostrar
- No está atado a los datos del servidor, de hecho probablemente tenga tan solo un subconjunto de dichos datos
- Aplicaciones complejas tenderán a tener varios viewmodels de knockout por cada modelo de Backbone
Por supuesto lo más importante es usar cada librería para lo que ha sido pensada y tener siempre bien claro que ventajas e inconvenientes nos puede tener usar cada una de ellas 🙂
Un saludo!
pd: Ah sí… y eso de las librerías de javascript está en constante evolución! No todo es Backbone o Knockout! Existen más por ahí afuera y en breve espero poder hablaros de alguna de ellas! 😉
Tuvo que ser un evento de lo más interesante, lástima que Barcelona me pille un poco lejos.
Una de las librerías que más me ha llamado la atención últimamente ha sido angularjs, sería genial que te animases a escribir algo sobre ella 🙂
Buenas Juanma!
A ver si podemos repetir el evento en otras ciudades! 😀
Pues honestamente, AngularJS, no la conocía. Pero precisamente esta mañana Hadi Hariri me ha recomendado por twitter que le eche un vistazo y he empezado a echarle una ojeada. Escribiré sobre ella, fijo ya que quiero hablar de esta y también probar EmberJS a ver que tal…
Un saludo y gracias por el comentario! 😉
No pasa nada por perder por KO, a ver si repetimos la experiencia en Madrid y te daré un poco de ventaja 😉