Visões do Backbone e o DOM
Portuguese (Português) translation by Erick Patrick (you can also view the original English article)



Visão Geral
Visões do Backbone provêm convenção e abstração úteis para interfaces de usuário. Contudo, para incluir funcionalidades de UI em aplicações que o Backbone, por conta própria, não foi criado pra suportar, é preciso considerar como integrar efetivamente funcionalidade customizada ou de terceiros nela. Como resultado, desenvolvedores devem navegar pelos desafios e evitar conflitos complicado entre bibliotecas externas e o Backbone.
Introdução ao Backbone.js
Backbone é uma forma fantástica de organizar código no lado do cliente. Com abstrações como modelos, visões e coleções, o Backbone ajuda desenvolvedores sérios a escrever aplicações bem organizadas e escaláveis.
Embora haja várias alterantivas ao Backbone, incluindo Angular e Ember, o Backbone dá liberdade aos desenvolvedores a escreverem e organizarem seus códigos de forma natural e confortável sem ser muito opinativa sobre como o Modelo de Objeto do Documento (DOM) se parece.
A Pele das Visões do Backbone
Visões são um dos componentes mais poderosos e flexíveis do Backbone. De acordo com os autores dele:
Visões do Backbone são mais convenção que código — não determinam padrões de HTML ou CSS por você e podem ser usadas com qualquer biblioteca de modelagem em JavaScript.
Ela são usadas para manipular o que os usuários veem no navegador e facilitam a comunicação entre os modelos. Como resultado, no paradigam Modelo-Visão-Controlador, imagine as Visões do Backbone como ambos visão e controlador.
Isso implica seriamente ao desenvolver aplicações com interações significarivas de usuários. Na verdade, há várias situações onde pode-se querer usar outra biblioteca para manipular o DOM. Visualização de dados e jogos de navegador são dois exemplos onde se pode preferir ter outra biblioteca para manipular parte da renderização da visão dos usuários. Assim, pode-se escolher jQuery, d3.js, crossfilter ou three.js para algumas das manipulações do DOM.
Felizmente, há formas de fazer o Backbone trabalhar bem com esses outros manipuladores de DOM.
Manipulando o Modelo de Objeto do Documento no Backbone
Antes de continuarmos, revisemos a manipulação do DOM com Backbone. Comecemos com um objeto de visão básico.
1 |
var SomeView = Backbone.View.extend({ |
2 |
// Some definition stuff here
|
3 |
});
|
4 |
|
5 |
var aView = new SomeView(); |
Ótimo. Agora, digamos à visão para renderizar-se, definindo o método .render()
.
1 |
var SomeView = Backbone.View.extend({ |
2 |
// define how to render something
|
3 |
render: function() { |
4 |
// get some HTML
|
5 |
var htmlContent = "<p>This some HTML that will be inserted into the DOM</p>"; |
6 |
|
7 |
// insert the html
|
8 |
this.$el.html(htmlContent); |
9 |
|
10 |
// return an instance of the object for chaining
|
11 |
return this; |
12 |
}
|
13 |
});
|
Temos várias coisas acontecendo aqui. Vejamos passo-a-passo.
Definindo um Método .render()
.
Primeiro, definimos um método .render()
que encapsula a lógica necessária para renderizar o HTML. Note que o Backbone vem com um método .render()
por padrão. Contudo, não significa muita coisa. Ele foi projetado para ser sobrescrito!
Obtendo Conteúdo HTML
O exemplo acima assume que obtemos o HTML de algum lugar. Podemos usar underscores _.template()
. Anternativamente, podemos usar outras bibliotecas de modelagem, como a Handlebars (minha favorita). O que importa é que, de alguma forma, obtenhamos o conteúdo HTML.
O Que Seria Esse el
?
Precisamos de um lugar para guardar o HTML. E é para isso que o el
serve. Como o .render()
, el
é um atributo que vem por padrão com as Visões do Backbone. Ele referencia o elemento HTML (e todos seus filhos) presente na visão. No exemplo acima, não o especificamos. Por padrão, el
é uma div
. Contudo, poderíamos ter um elemento pai assim:
1 |
var SomeView = Backbone.View.extend({ |
2 |
el: "article", ... |
3 |
});
|
4 |
|
5 |
var aView = new SomeView(); |
6 |
|
7 |
console.log(aView.el); // an empty "article" HTML element |
Também existe o $el
, que apenas é o el
envolvido pelo jQuery Depois veremos que $el
tem um papel importante ao dominar as Visões do Backbone.
Retornando this
...
Finalmente, retornarmos a referência do objeto em si, permitindo encadeamento. Embora não necessário, retornar this
é convenção. Sem return this
, precisaríamos acessar o conteúdo do HTML de alguma forma. O código abaixo ilustra uma solução alternativa.
1 |
/**
|
2 |
* If render() returns nothing, we are really
|
3 |
* accessing the `el` property of undefined, which does not
|
4 |
* exist!
|
5 |
*/
|
6 |
aView.render().el; // Should throw an error |
7 |
|
8 |
// Try accessing the HTML
|
9 |
console.log(aView.el); // Should be empty (but defined!) |
10 |
|
11 |
// add HTML to the DOM of 'aView'
|
12 |
aView.render(); |
13 |
|
14 |
// Try accessing the HTML again
|
15 |
console.log(aView.el) // Should contain the HTML |
Ihh, Nada Aparece na Tela!
Bom ponto. Embora tenhamos chamado .render()
, ainda não vemos algo na tela—que coisa?!
Isso se dá por não termos interagiado com o DOM ainda. Tudo que fizemos foi gerar HTML e representá-lo em um objeto JavaScript chamado aView
. Como não temos acesso ao HTML gerado, o que precisamos fazer é inserir o HTML no DOM do nosso aplicativo web.
Para continuar, criaremos um pequenos app que fará a visão aparecer assim que a página carregar. Abaixo temos o HTML e o JavaScript necessários:
Preparação do HTML
1 |
<html>
|
2 |
<head>
|
3 |
<meta charset="utf-8"> |
4 |
<title>My Awesome Backbone App</title> |
5 |
<!-- Include your CSS Here -->
|
6 |
<link rel="stylesheet" type="text/css" href="/css/styles.css" /> |
7 |
|
8 |
<!-- Include JS dependencies -->
|
9 |
<!-- Backbone depends on underscore and, in this example,
|
10 |
depends on jQuery. Please note the order of the
|
11 |
dependencies -->
|
12 |
<script src="/js/lib/jquery.js"></script> |
13 |
<script src="/js/lib/underscore.js"></script> |
14 |
<script src="/js/lib/backbone.js"></script> |
15 |
</head>
|
16 |
<body>
|
17 |
<div class="app"></div> |
18 |
<!-- Include your custom Backbone code in the below script -->
|
19 |
<script src="/js/app.js"></script> |
20 |
</body>
|
21 |
</html>
|
Eis O Que Temos no App.js
1 |
// Create a view
|
2 |
var SomeView = Backbone.View.extend({ |
3 |
initialize: function() {}, |
4 |
render: function() { |
5 |
var someHTML = "<p>This is some HTML</p>"; |
6 |
this.$el.html(someHTML); |
7 |
return this; |
8 |
}
|
9 |
});
|
10 |
|
11 |
// Create a router
|
12 |
var Router = Backbone.Router.extend({ |
13 |
// define your routes
|
14 |
routes: { "": "home" }, |
15 |
home: function() { |
16 |
var aView = new SomeView(); |
17 |
$('.app').html(aView.render().el); |
18 |
}
|
19 |
});
|
20 |
|
21 |
// Instantiate your router
|
22 |
new Router(); |
23 |
|
24 |
// Start tracking history
|
25 |
Backbone.history.start(); |
Abramos um servidor local/navegador, carreguemos a página e a aplicação deverá rodar!
Usando Backbone e jQuery Simultaneamente
A flexibilidade do Backbone permite-nos usar bibliotecas de terceiro para manipular o DOM. Um cenário é quando queremos usar jQuery e Backbone simultaneamente para manipular as visões. Abaixo temos o exemplo atualizado:
1 |
var SomeView = Backbone.View.extend({ |
2 |
|
3 |
// Manipulate DOM indirectly by creating HTML content in a
|
4 |
// Backbone View
|
5 |
render: function() { |
6 |
var someHTML = "<p>Some HTML</p><p class='empty'><p>"; |
7 |
this.$el.html(someHTML); |
8 |
return this; |
9 |
},
|
10 |
|
11 |
// Manipulate DOM directly from within the Backbone View
|
12 |
renderWithJQuery: function() { |
13 |
var otherHTML = "<p>Other HTML</p>"; |
14 |
$('.app').append(otherHTML); |
15 |
|
16 |
// may not make sense to return 'this'
|
17 |
},
|
18 |
|
19 |
// another render method, to keep things interesting
|
20 |
specialRender: function() { |
21 |
this.$('.empty').append("<span>No longer empty!</span>"); |
22 |
return this; |
23 |
}
|
24 |
});
|
25 |
|
26 |
// Later in your app...
|
27 |
|
28 |
// create the view
|
29 |
var aView = new SomeView(); |
30 |
|
31 |
// change the DOM to reflect the newly created view
|
32 |
$('.app').html(aView.render().el); |
33 |
|
34 |
// append more content directly to the DOM using jQuery within
|
35 |
// a Backbone view object
|
36 |
aView.renderWithJQuery(); |
O código resultará em dois parágrafos na página. O primeiro parágrafo conteém "Some HTML". O segundo contém "Other HTML".
Para testar o conhecimento, invertamos as chamadas, assim:
1 |
// SomeView is already defined
|
2 |
|
3 |
var aView = new SomeView(); |
4 |
|
5 |
aView.renderWithJQuery(); |
6 |
|
7 |
$('.app').html(aView.render().el); |
O cüodigo acima resultará em um parágrafo: "Algum HTML". Em ambos os casos, há um elemento <p>
com nada nele. Discutiremos isso em já-já.
Manipulando o DOM Eficientemente com Visões do Backbone
Entender a mágica da manipulação (travessia) eficiente do DOM requer entendimento de this.$el
e this.$()
. Ao usar this.$el
, avaliamos a manipulação do DOM do conteúdo dentro da visão. Já com this.$()
, avaliamos a travessia do DOM à arvore DOM dentro da visão.
Assim, no Backbone, usar $()
(ao invés de this.$()
) podem ser ineficiente. Por exemplo, digamos que queremos atravessar o DOM para encontrar um elemento. Podemoss usar quaisquer dos métodos de travessia do DOM, incluindo .find()
, .children()
, .closest()
, .first()
, e por aí vai.
Se soubermos, a priori, que o elemento procurado está no DOM da visão, devemos usar this.$()
, evitando uma busca desnecessária num DOM maior. Se o elemento estiver fora do DOM da visão, temos de usar $()
.
Por exemplo, o método .specialRender()
usa travessia localizada do DOM para garantir que buscamos elementos com a classe empty
no contexto da visão. Se encontrada, faz o HTML desses elementos incluir um span
e o texto "No longer empty".
Conclusão
Nese artigo, revisamos as Visões do Backbone, discutimos como renderizá-las no DOM e exploramos como fazer o Backbone trabalhar junto de outras biblotecas que queiramos usar para manipular o DOM. Também aprendermos sobre travessia localizada do DOM e identificamos métodos eficiente e ineficientes para atravessá-lo.
Na próxima parte, aprofundaremo-nos em exemplos mais complicados emcomo fazer múltiplas bibliotecas trabalharem juntas para manipular o DOM.