Ao desenvolver um aplicativo web que você vai inevitavelmente trabalhar muito com a linguagem javascript para lidar a manipulação DOM ou para algumas outras necessidades.

Existem muitas bibliotecas JavaScript que irão tornar essa tarefa mais fácil, a biblioteca mais popular, como todos sabemos é a Jquery. Mas quando a nossa aplicação fica maior o código terá uma estrutura para torná-lo fácil de manter, absolutamente jQuery por si só não pode fazer isso. É quando Backbone vem para jogar.

O que é Backbone.js?

Backbone.js é uma biblioteca javascript que fornece estrutura para as suas aplicações, adapta-se o conceito de MVC (model, view, controller) padrão que foi implementado no início da linguagem server-side como PHP.

Mas a espinha dorsal é considerado não um verdadeiro MVC, é considerado como MV * porque C no backbone é para a coleção em vez de Controller. Eu posso dizer-lhe que ainda lhe dá a funcionalidade similar.

Para aqueles que não estão familiarizados com os termos de padrão MVC deixe-me dar uma breve descrição:

  • M significa "Model", no backbone É simplesmente um objeto que representa os dados.
  • V significa "View", visualização é responsável para lidar com o que servir/exibir para o usuário. Ver está ligado a dados em conjunto ou modelo.
  • C em backbone é "Coleção" como uma mencionado anteriormente. Collection é a coleção de modelo, o seu responsável para lidar com um conjunto de dados (que foi armazenado no modelo).

A ideia de MVC é uma separação de preocupação. Essa ideia é razoavelmente boa, porque se você manter grandes códigos de sua aplicação, você pode editar algumas partes do seu código sem afetar as outras. Por exemplo, se você precisa atualizar a interface do usuário do seu aplicativo, você só vai precisar alterar os códigos dentro de uma visão sem a necessidade de mudar o modelo ou coleção. Você pode ler mais sobre o padrão MVC aqui.

É a substituição jQuery?

O Backbone não substitui o jQuery, com ele é perfeitamente possível se trabalhar com outras bibliotecas como o jQuery ou Zepto.

Quando eu preciso Backbone.js?

Se você estiver desenvolvendo uma aplicação web que exige muito javascript, você terá backbone. Se seu aplicativo precisa ser escalável, então você vai precisar de backbone para dar estrutura ao seu código. Se você está construindo uma aplicação web fantástica e trabalha muito com linhas de jQuery para atravessar o DOM, ou dar animações, muitos eventos de ligação para o seu aplicativo, você vai com certeza querer usar o Backbone.

Se você estiver criando um tema para wordpress, provavelmente irá precisar de um pouco de jQuery, você não precisa usar backbone.

A conclusão é que, se você quiser trazer o seu front-end para o próximo nível, um nível mais elevado, você vai precisar usar backbone.

O que eu preciso saber antes de trabalhar com backbone?

Este conselho é apenas para novatos. Muitas pessoas entendem como usar a biblioteca javascript como jQuery, mas isso não é suficiente para entender como backbone funciona. Você precisa saber sobre a programação orientada a objetos.

Ok, eu estou realmente interessado com backbone. Então, como posso instalá-lo?

A instalação é simples e direta, como quando você instala o jQuery, você vai chamar a biblioteca em algum lugar em sua aplicação. Backbone depende de um cinto de utilidades chamado underscore.js. Portanto, antes de chamar biblioteca backbone tenha certeza de que você precisa chamar o underscore.js em primeiro lugar. Veja a listagem abaixo:


<!DOCTYPE html>
<html>
<head>
<title>Backbone App</title>
</head>
<body>
        <script src="js/jquery.js"> </script>
<script src="js/underscore.js"></script>
<script src="js/backbone.js"></script>
</body>
</html>
Listagem 1. Configurando o requisito mínimo para trabalhar com backbone
Nota:

Faça o download do underscore.js aqui.

Faça o download do Backbone.js aqui.

Depois de baixar todos os arquivos necessários, coloque os arquivos em uma pasta chamada 'js' na raiz da sua aplicação e chame o script como a listagem acima. Lembre-se de que o underscore.js deve ser chamado antes de backbone.js.

Backbone.js como eu mencionei anteriormente, fornece a estrutura para o seu código, implementando o padrão *MV no lado do cliente.

Deixe-me mostrar-lhe uma pequena diferença em delegações de eventos entre jQuery e Backbone.


$(‘div.open’).click(function(){ .... seu codigo aqui ..... });
Listagem 2. delegação de eventos usando jQuery

//this code is inside a backbone view object
events : {
“click div.open” : “openDialog”  //if user clicks ‘div’ with class 
‘open’ run the ‘openDialog’ methods
},
openDialog : function () { ... you code goes here ...});  
//define your openDialog methods as callback
Listagem 3. delegação de eventos usando Backbone

Não é tão grande a diferença entre eles. Enquanto jQuery fornece mecanismo de encadeamento de reta que provavelmente vai lhe dar os códigos de espaguete como o aplicativo se torna maior. Backbone oferece abordagem mais estrutural, onde eventos delegações é ligar de propriedade dos "eventos" dentro de um objeto de exibição e separar o callback em outros métodos. Vamos ver mais um exemplo de delegações de eventos usando backbone.


//esse codigo vai dentro do objeto backbone 
events : {
“click div.open” : “openDialog” 
    “dblclick span.edit” : “editPost” 
    “click a.close” : “closePost” 
},
// aqui define os callbacks 
openDialog : function (e) { ... codigo aqui ..},
editPost: function(e) { ... codigo aqui... },
closePost: function(e) { ... codigo aqui.. }
Listagem 4. Delegação vários eventos usando Backbone

Outros exemplos usando backbone.js

Abaixo iremos ver outros exemplos de código onde o backbone.js é utilizado.


var HelloView = Backbone.View.extend({
    el: $('body'),
    initialize: function() {
        this.render();
    },
    render: function() {
        $(this.el).append("<h1>Hello World</h1>");
    }
});
Listagem 5. Criando a classe view de um hello world

Após criar a classe, o mais importante será instanciar a view e o método initialize que ocorrerá automaticamente, inserindo o H1 ao body.


var view = new HelloView();
Listagem 6. Instanciando a view

O código completo pode ser visto na listagem 7.


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello World</title>
        <script src="../lib/jquery-min.js"></script>
        <script src="../lib/underscore-min.js"></script>
        <script src="../lib/backbone-min.js"></script>
        <script>
            $(document).ready(function() {
                var HelloView = Backbone.View.extend({
                    el: $('body'),
                    initialize: function() {
                        this.render();
                    },
                    render: function() {
                        $(this.el).append("<h1>Hello World</h1>");
                    }
                });
                var helloView = new HelloView();
            });
        </script>
    </head>
    <body></body>
</html>
Listagem 7. Código completo do hello world

Criando uma aplicação de to-do

Que tal aprendermos a criar uma aplicação de to-do list usando essa maravilhosa ferramenta?

Nota: O código a seguir foi criado por Jerome Gravel-Niquet e está disponível também no GitHub.

Primeiro vamos ao código html da ferramenta


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Backbone.js Todos</title>
  <link rel="stylesheet" href="todos.css"/>
  <script type="text/javascript">

    var _gaq = _gaq || [];
    _gaq.push(['_setAccount', 'UA-5201171-9']);
    _gaq.push(['_trackPageview']);

    (function() {
      var ga = document.createElement('script'); ga.type = 
      'text/javascript'; ga.async = true;
      ga.src = ('https:' == document.location.protocol ? 
      'https://ssl' : 'http://www') +
  '.google-analytics.com/ga.js';
      var s = document.getElementsByTagName('script')[0]; 
      s.parentNode.insertBefore(ga, s);
    })();

  </script>
</head>

<body>

  <div id="todoapp">

    <header>
      <h1>Todos</h1>
      <input id="new-todo" type="text" 
      placeholder="What needs to be done?">
    </header>

    <section id="main">
      <input id="toggle-all" type="checkbox">
      <label for="toggle-all">Mark all 
      as complete</label>
      <ul id="todo-list"></ul>
    </section>

    <footer>
      <a id="clear-completed">Clear 
      completed</a>
      <div id="todo-count"></div>
    </footer>

  </div>

  <div id="instructions">
    Double-click to edit a todo.
  </div>

  <div id="credits">
    Created by
    <br />
    <a href="http://jgn.me/">Jérôme Gravel-Niquet</a>.
    <br />Rewritten by: <a 
    href="http://addyosmani.github.com/todomvc">TodoMVC</a>.
  </div>

  <script src="../../spec/support/jquery.js"></script>
  <script src="../../spec/support/underscore.js"></script>
  <script src="../../spec/support/backbone.js"></script>
  <script src="../../backbone.localStorage.js"></script>
  <script src="todos.js"></script>

  <!-- Templates -->

  <script type="text/template" id="item-template">
    <div class="view">
      <input class="toggle" type="checkbox" <%= 
      done ? 'checked="checked"' : '' %> />
      <label><%- title %></label>
      <a class="destroy"></a>
    </div>
    <input class="edit" type="text" value="<%- 
    title %>" />
  </script>

  <script type="text/template" id="stats-template">
    <% if (done) { %>
      <a id="clear-completed">Clear <%= done %> 
      completed <%= done == 1 ? 'item' : 'items' %></a>
    <% } %>
    <div class="todo-count"><b><%= remaining 
    %></b> <%= remaining == 1 ? 'item' : 'items' 
    %> left</div>
  </script>

  </body>
</html>
Listagem 8. Código html da ferramenta de to-do

Após isso, temos o código css para estilizar a aplicação.


html,
body {
	margin: 0;
	padding: 0;
}

body {
	font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
	line-height: 1.4em;
	background: #eeeeee;
	color: #333333;
	width: 520px;
	margin: 0 auto;
	-webkit-font-smoothing: antialiased;
}

#todoapp {
	background: #fff;
	padding: 20px;
	margin-bottom: 40px;
	-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
	-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
	-ms-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
	-o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
	box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
	-webkit-border-radius: 0 0 5px 5px;
	-moz-border-radius: 0 0 5px 5px;
	-ms-border-radius: 0 0 5px 5px;
	-o-border-radius: 0 0 5px 5px;
	border-radius: 0 0 5px 5px;
}

#todoapp h1 {
	font-size: 36px;
	font-weight: bold;
	text-align: center;
	padding: 0 0 10px 0;
}

#todoapp input[type="text"] {
	width: 466px;
	font-size: 24px;
	font-family: inherit;
	line-height: 1.4em;
	border: 0;
	outline: none;
	padding: 6px;
	border: 1px solid #999999;
	-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
	-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
	-ms-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
	-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
	box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
}

#todoapp input::-webkit-input-placeholder {
	font-style: italic;
}

#main {
	display: none;
}

#todo-list {
	margin: 10px 0;
	padding: 0;
	list-style: none;
}

#todo-list li {
	padding: 18px 20px 18px 0;
	position: relative;
	font-size: 24px;
	border-bottom: 1px solid #cccccc;
}

#todo-list li:last-child {
	border-bottom: none;
}

#todo-list li.done label {
	color: #777777;
	text-decoration: line-through;
}

#todo-list .destroy {
	position: absolute;
	right: 5px;
	top: 20px;
	display: none;
	cursor: pointer;
	width: 20px;
	height: 20px;
	background: url(destroy.png) no-repeat;
}

#todo-list li:hover .destroy {
  	display: block;
}

#todo-list .destroy:hover {
  	background-position: 0 -20px;
}

#todo-list li.editing {
	border-bottom: none;
	margin-top: -1px;
	padding: 0;
}

#todo-list li.editing:last-child {
	margin-bottom: -1px;
}

#todo-list li.editing .edit {
	display: block;
	width: 444px;
	padding: 13px 15px 14px 20px;
	margin: 0;
}

#todo-list li.editing .view {
	display: none;
}

#todo-list li .view label {
	word-break: break-word;
}

#todo-list li .edit {
	display: none;
}

#todoapp footer {
	display: none;
	margin: 0 -20px -20px -20px;
	overflow: hidden;
	color: #555555;
	background: #f4fce8;
	border-top: 1px solid #ededed;
	padding: 0 20px;
	line-height: 37px;
	-webkit-border-radius: 0 0 5px 5px;
	-moz-border-radius: 0 0 5px 5px;
	-ms-border-radius: 0 0 5px 5px;
	-o-border-radius: 0 0 5px 5px;
	border-radius: 0 0 5px 5px;
}

#clear-completed {
	float: right;
	line-height: 20px;
	text-decoration: none;
	background: rgba(0, 0, 0, 0.1);
	color: #555555;
	font-size: 11px;
	margin-top: 8px;
	margin-bottom: 8px;
	padding: 0 10px 1px;
	cursor: pointer;
	-webkit-border-radius: 12px;
	-moz-border-radius: 12px;
	-ms-border-radius: 12px;
	-o-border-radius: 12px;
	border-radius: 12px;
	-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
	-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
	-ms-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
	-o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
	box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
}

#clear-completed:hover {
	background: rgba(0, 0, 0, 0.15);
	-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
	-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
	-ms-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
	-o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
	box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
}

#clear-completed:active {
	position: relative;
	top: 1px;
}

#todo-count span {
	font-weight: bold;
}

#instructions {
	margin: 10px auto;
	color: #777777;
	text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
	text-align: center;
}

#instructions a {
	color: #336699;
}

#credits {
	margin: 30px auto;
	color: #999;
	text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
	text-align: center;
}

#credits a {
	color: #888;
}
Listagem 9. Código css de estilização

Feito isso, a única coisa que fica faltando é o código javascript, não é mesmo ? Veja a listagem 10.


// An example Backbone application contributed by
// [Jérôme Gravel-Niquet](http://jgn.me/). 
This demo uses a simple
// [LocalStorage adapter](backbone-localstorage.html)
// to persist Backbone models within your browser.

// Load the application once the DOM is ready, 
using `jQuery.ready`:
$(function(){

  // Todo Model
  // ----------

  // Our basic **Todo** model has `title`, 
  `order`, and `done` attributes.
  var Todo = Backbone.Model.extend({

    // Default attributes for the todo item.
    defaults: function() {
      return {
        title: "empty todo...",
        order: Todos.nextOrder(),
        done: false
      };
    },

    // Ensure that each todo created has `title`.
    initialize: function() {
      if (!this.get("title")) {
        this.set({"title": this.defaults().title});
      }
    },

    // Toggle the `done` state of this todo item.
    toggle: function() {
      this.save({done: !this.get("done")});
    }

  });

  // Todo Collection
  // ---------------

  // The collection of todos is backed by 
  *localStorage* instead of a remote
  // server.
  var TodoList = Backbone.Collection.extend({

    // Reference to this collection's model.
    model: Todo,

    // Save all of the todo items under the 
    `"todos-backbone"` namespace.
    localStorage: new Backbone
    .LocalStorage("todos-backbone"),

    // Filter down the list of all todo items 
    that are finished.
    done: function() {
      return this.filter(function(todo){ return 
      odo.get('done'); });
    },

    // Filter down the list to only todo items that are 
    still not finished.
    remaining: function() {
      return this.without.apply(this, this.done());
    },

    // We keep the Todos in sequential order, despite being 
    saved by unordered
    // GUID in the database. This generates the next order 
    number for new items.
    nextOrder: function() {
      if (!this.length) return 1;
      return this.last().get('order') + 1;
    },

    // Todos are sorted by their original insertion order.
    comparator: function(todo) {
      return todo.get('order');
    }

  });

  // Create our global collection of **Todos**.
  var Todos = new TodoList;

  // Todo Item View
  // --------------

  // The DOM element for a todo item...
  var TodoView = Backbone.View.extend({

    //... is a list tag.
    tagName:  "li",

    // Cache the template function for a single item.
    template: _.template($('#item-template').html()),

    // The DOM events specific to an item.
    events: {
      "click .toggle"   : "toggleDone",
      "dblclick .view"  : "edit",
      "click a.destroy" : "clear",
      "keypress .edit"  : "updateOnEnter",
      "blur .edit"      : "close"
    },

    // The TodoView listens for changes to its model, 
    re-rendering. Since there's
    // a one-to-one correspondence between a **Todo** 
    and a **TodoView** in this
    // app, we set a direct reference on the model for 
    convenience.
    initialize: function() {
      this.listenTo(this.model, 'change', this.render);
      this.listenTo(this.model, 'destroy', this.remove);
    },

    // Re-render the titles of the todo item.
    render: function() {
      this.$el.html(this.template(this.model.toJSON()));
      this.$el.toggleClass('done', this.model.get('done'));
      this.input = this.$('.edit');
      return this;
    },

    // Toggle the `"done"` state of the model.
    toggleDone: function() {
      this.model.toggle();
    },

    // Switch this view into `"editing"` mode, displaying 
    the input field.
    edit: function() {
      this.$el.addClass("editing");
      this.input.focus();
    },

    // Close the `"editing"` mode, saving changes to the todo.
    close: function() {
      var value = this.input.val();
      if (!value) {
        this.clear();
      } else {
        this.model.save({title: value});
        this.$el.removeClass("editing");
      }
    },

    // If you hit `enter`, we're through editing the item.
    updateOnEnter: function(e) {
      if (e.keyCode == 13) this.close();
    },

    // Remove the item, destroy the model.
    clear: function() {
      this.model.destroy();
    }

  });

  // The Application
  // ---------------

  // Our overall **AppView** is the top-level piece of UI.
  var AppView = Backbone.View.extend({

    // Instead of generating a new element, bind to 
    the existing skeleton of
    // the App already present in the HTML.
    el: $("#todoapp"),

    // Our template for the line of statistics at 
    the bottom of the app.
    statsTemplate: _.template($('#stats-template').html()),

    // Delegated events for creating new items, and 
    clearing completed ones.
    events: {
      "keypress #new-todo":  "createOnEnter",
      "click #clear-completed": "clearCompleted",
      "click #toggle-all": "toggleAllComplete"
    },

    // At initialization we bind to the relevant events 
    on the `Todos`
    // collection, when items are added or changed. 
    Kick things off by
    // loading any preexisting todos that might be saved 
    in *localStorage*.
    initialize: function() {

      this.input = this.$("#new-todo");
      this.allCheckbox = this.$("#toggle-all")[0];

      this.listenTo(Todos, 'add', this.addOne);
      this.listenTo(Todos, 'reset', this.addAll);
      this.listenTo(Todos, 'all', this.render);

      this.footer = this.$('footer');
      this.main = $('#main');

      Todos.fetch();
    },

    // Re-rendering the App just means refreshing the 
    statistics -- the rest
    // of the app doesn't change.
    render: function() {
      var done = Todos.done().length;
      var remaining = Todos.remaining().length;

      if (Todos.length) {
        this.main.show();
        this.footer.show();
        this.footer.html(this.statsTemplate({done: done, 
        remaining: remaining}));
      } else {
        this.main.hide();
        this.footer.hide();
      }

      this.allCheckbox.checked = !remaining;
    },

    // Add a single todo item to the list by creating a 
    view for it, and
    // appending its element to the `<ul>`.
    addOne: function(todo) {
      var view = new TodoView({model: todo});
      this.$("#todo-list").append(view.render().el);
    },

    // Add all items in the **Todos** collection at once.
    addAll: function() {
      Todos.each(this.addOne, this);
    },

    // If you hit return in the main input field, create new 

    **Todo** model,
    // persisting it to *localStorage*.
    createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      if (!this.input.val()) return;

      Todos.create({title: this.input.val()});
      this.input.val('');
    },

    // Clear all done todo items, destroying their models.
    clearCompleted: function() {
      _.invoke(Todos.done(), 'destroy');
      return false;
    },

    toggleAllComplete: function () {
      var done = this.allCheckbox.checked;
      Todos.each(function (todo) { todo.save({'done': done}); });
    }

  });

  // Finally, we kick things off by creating the **App**.
  var App = new AppView;

});
Listagem 10. Código Javascript da aplicação de to-do

Com esses 3 códigos será possível vocês fazerem uma ferramenta de To-do onde você pode editar e alterar de acordo com a sua necessidade em seu projeto. Estou disponibilizando para download os arquivos dessa ferramenta no topo desse artigo.

Conclusão

Lembre-se, eu disse que backbone depende do underscore, isso significa que você também pode usar o poder do underscore dentro do backbone. Para saber mais sobre o assunto, sugiro que olhe o site oficial do underscore e veja a mágica que o backbone pode fazer.