Por que eu devo ler este artigo:Este artigo é útil por apresentar uma série de conceitos importantes acerca do framework JavaScript Vue.js, o qual fornece insumos para facilitar o desenvolvimento de aplicações escaláveis, reaproveitando código e estruturas similares. Veremos, através da construção de uma aplicação de listagem de livros, a instalação, configuração, os principais recursos, facilitadores, listeners e muito mais.

O Vue.js se adéqua a diferentes cenários de programação e revelou-se um framework poderoso e extremamente útil no dia a dia dos programadores front.


Guia do artigo:

Apresentando o Vue.Js

Quando Evan You, criador do Vue.js, estava trabalhando em um dos projetos do Google Creative Labs, ele precisava de uma interface de protótipo para UIs bem grandes. Claramente, escrever uma grande quantidade de HTML repetido era consumir tempo e recursos, e é por isso que Evan começou a procurar alguma ferramenta já existente para essa finalidade.

Para sua surpresa, descobriu que não havia nenhuma ferramenta, biblioteca ou framework que se adequasse exatamente ao seu propósito de prototipagem rápida! Naquela época, o AngularJS era amplamente utilizado, o React.js estava apenas começando, e frameworks como Backbone.js eram usados ​​para aplicações em larga escala com arquitetura MVC. Para o tipo de projeto que precisava de algo realmente flexível e leve apenas para uma rápida prototipagem UI, nenhuma dessas estruturas complexas foram adequadas. Quando você percebe que algo adequado não existe e você é capaz de criá-lo, então essa é a hora de fazê-lo!

E foi assim que surgiu a ideia de criar um framework que ajudaria na prototipagem rápida, oferecendo uma maneira fácil e flexível de ligação de dados reativos e componentes reutilizáveis. Agora ele pode ser usado para construir complexos aplicativos escaláveis ​​e reativos na web.

Como todo bom framework, o Vue.js vem crescendo e evoluindo, proporcionando cada vez mais recursos em relação às suas primeiras versões. Atualmente, ele fornece uma maneira fácil de anexar e criar plugins, escrever e usar mixins, e adicionar comportamento personalizado. É possível, inclusive, usá-lo de uma forma tão flexível para a estruturação de aplicativos que ele definitivamente pode ser considerado um framework capaz de suportar a construção de aplicações web complexas.

O Vue.js permite que você vincule facilmente seus modelos de dados à camada de apresentação. Ele também permite reutilizar componentes em toda a aplicação, sem precisar criar modelos especiais ou coleções e registrar eventos no objeto. Você não precisa seguir nenhuma sintaxe especial, nem instalar nenhuma das intermináveis ​​dependências que frameworks convencionais exigem.

Seus modelos são objetos JavaScript simples, que podem ser vinculados a tudo o que você quer em suas Views (texto, input texts, classes, atributos, e assim por diante). Vale citar também que você pode separar a camada de View dos estilos e lógica JavaScript, ou pode colocá-los juntos no mesmo arquivo e construir a estrutura de seus componentes e lógica no mesmo lugar.

A usabilidade é mais simples ainda: basta adicionar o arquivo vue.js ao seu projeto e usá-lo. Alternativamente, você pode usar o vue-cli (a interface de linha de comando do Vue) com o Webpack e a família do Browserify, que não apenas constrói todo o projeto, mas também suporta hot reload (recarga automática das alterações no código em tempo real).

Há um suporte de plug-in para todos os IDEs modernos e comumente usados, logo fique à vontade para selecionar o que melhor se adéqua às suas preferências. Você pode usar qualquer pré-processador que quiser, além de poder usar o ES2015. Vale citar também que é possível usar o Vue.js em conjunto com seu framework favorito ou até mesmo com algum que venha a estar construindo. A escolha é sua, o Vue.js te dá todo o resto.

Para entender melhor como o framework funciona, trataremos de desenvolver um app de lista de livros que você deseja ler. O mesmo servirá de referência prática à aplicabilidade dos recursos disponibilizados pelo framework. Será uma implementação simples que permitirá a inclusão e remoção dos itens, de modo similar a uma lista de tarefas ou aplicações de mesma finalidade. E para linearizar melhor os conhecimentos, apresentaremos a implementação em três etapas: 1) desenvolvimento do exemplo usando apenas HTML, CSS, Bootstrap, JavaScript e jQuery; 2) migração do exemplo para o Vue.js; 3) criação de componentes Vue.js reaproveitáveis com base no exemplo.

O que é o Vue.js?

O Vue.js (comumente conhecido como Vue, pronunciado "view") é um framework progressivo do JavaScript de código aberto (open source) para a construção de interfaces de usuário. A integração em projetos que usam outras bibliotecas de JavaScript é facilitada com o Vue porque ele foi projetado para ser adotado de forma incremental. O Vue também pode funcionar como uma estrutura de aplicativos web capaz de alimentar aplicativos avançados de uma única página.

De acordo com uma pesquisa do JavaScript de 2016, o Vue possui uma média de aprovação dos desenvolvedores de 89%. Recebe cerca de 95 estrelas no GitHub por dia e é o 10º projeto mais votado no GitHub de todos os tempos.

História do Vue.Js

O Vue foi criado por Evan You depois de trabalhar para o Google no AngularJS. Mais tarde, resumiu seu processo de pensamento: "Imaginei, e se eu pudesse extrair a parte que realmente gostava de Angular e construir algo realmente leve sem todos os conceitos extras envolvidos?"

O Vue foi originalmente lançado em fevereiro de 2014 por Evan You. O projeto foi postado no Hacker News, Echo JS e o / r / javascript subreddit no dia da sua versão inicial. Dentro de um dia, o projeto atingiu a primeira página dos três sites.

Mais recentemente, o Vue foi apresentado como uma grande promessa no GitHub, tendo obtido a maioria das estrelas de qualquer projeto de código aberto no popular site. Registou recentemente 57,908 Estrelas, que o torna entre os projetos open source mais populares no GitHub e a segunda estrutura/biblioteca de JavaScript mais popular (após React). Recentemente, superou outras bibliotecas antigas e mais estabelecidas, como Backbone.js

Vue.js CDN

Para propósitos de prototipação ou aprendizado, você pode usar a versão mais recente com:

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

Para produção, recomendados vincular a um número de versão específico para evitar quebra de funções das versões mais novas:

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>

Se você está usando Modulos ES nativos, existe uma build compatível com isso:


<script type="module">
  import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.esm.browser.js'
</script>

Você pode navegar pelos códigos-fonte do pacote NPM em cdn.jsdelivr.net/npm/vue.

Vue também está disponível no unpkg e no cdnjs (cdnjs leva algum tempo para sincronizar, então a última versão lançada pode não estar disponível ainda).

Tenha certeza de ler sobre as diferentes distribuições do Vue e usar a versão para produção em seu projeto publicado, alterando vue.js por vue.min.js. Este é um pacote menor otimizado para desempenho ao invés de experiência de desenvolvimento.

NPM

NPM é o método de instalação recomendado para construção de aplicações em larga escala com o Vue. Ele combina perfeitamente com empacotadores de módulos, tais como Webpack ou Browserify. Vue também oferece ferramentas para a criação de Componentes Single-File.


# última versão estável
$ npm install vue

Configurando ambiente

Como dito, nosso primeiro exemplo fará uso de tecnologias simples e já bem conhecidas no mundo de desenvolvimento web: HTML, CSS, Bootstrap, JavaScript e jQuery. O Bootstrap será usado para tornar a construção da estrutura e estilo da página mais simples, sem termos de nos preocupar com configurações que não fazem parte do escopo do artigo. Já o jQuery será usado para facilitar a codificação em JavaScript.

Comecemos então pela criação de uma estrutura mínima de organização dos diretórios e arquivos web, tal como:


  + /projeto
  +---- /css
  +     |----- index.css
  +---- /js
  +     |----- index.js
  +---- index.html

Isso será importante para identificar melhor cada parte de seu código. Agora, abra o arquivo index.html e inclua o conteúdo da Listagem 1 ao mesmo.

Listagem 1. Conteúdo inicial do index.html


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Lista de livros</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
    <link rel="stylesheet" href="css/index.css" />
  </head>

  <body>
    <div class="container">
      <h2>Minha lista de livros</h2>
      <div class="input-group">
        <input placeholder="adicione um livro" type="text" class="js-novo-livro form-control">
        <span class="input-group-btn">
            <button  @click="addItem" class="js-add btn btn-default" type="button">Adicionar!</button>
          </span>
      </div>
      <ul>
        <li>
          <div class="checkbox">
            <label>
                <input class="js-livro" name="list" type="checkbox" />
                <big>Alice no País das Maravilhas</big> - <small>Lewis Carrol</small>
              </label>
          </div>
        </li>
        <li>
          <div class="checkbox">
            <label>
                <input class="js-livro" name="list" type="checkbox">
                <big>A Menina que Roubava Livros</big> - <small>Markus Zusak</small>
              </label>
          </div>
        </li>
        <li class="removido">
          <div class="checkbox">
            <label>
                <input class="js-livro"  name="list"  type="checkbox" checked>
                <big>O Senhor dos Anéis - As Duas Torres</big> - <small>J. R. R. Tolkien</small>
              </label>
          </div>
        </li>
      </ul>
    </div>
  </body>
  <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
  <script type="text/javascript" src="js/index.js"></script>
</html>

A listagem é importante pois contextualiza todo o aparato de código que precisamos, e que qualquer aplicação convencional carrega consigo, com o intuito de expor todo o poder de integração que o Vue.js pode prover. Vejamos alguns de seus principais detalhes:

  • Tag <head> (linhas 3 a 8): aqui configuramos propriedades básicas da página, como o charset, e importamos os arquivos de CSS que irão adicionar estilos prontos à mesma: o CSS minificado do Bootstrap (importado via CDN) e o arquivo index.css (criado no início do projeto para carregar nossas configurações próprias de estilo. Certifique-se de tê-lo criado no diretório correto);
  • Div container (linha 11): conterá o corpo da página como um todo. É amplamente usada pelo Bootstrap para agrupar seus elementos de forma hierárquica;
  • Div input-group(linha 13): contém o campo de texto que usaremos para receber o valor do novo livro, bem como o botão para efetuar a chamada JavaScript de inserção. Observe que na tag <button> estamos usando uma propriedade não-HTML: o @click. Essa pertence ao Vue.js e a analisaremos mais adiante, portanto não se preocupe com a sua presença na página por enquanto;
  • Lista ul (linhas 19 a 44): lista que usaremos para inserir alguns elementos com estilo pré-estabelecido, o mesmo que será usado pelo JavaScript no momento de inserir novos livros na lista. Também estamos criando os checkboxes, cujas propriedades name devem ser iguais para que o HTML entenda como uma lista só. Na linha 36, todavia, temos um exemplo de item removido (isto é, livro lido) da lista, cuja classe CSS removido demarcará um estilo característico em nosso index.css;
  • Arquivos JS (linhas 47 e 48): aqui importamos o arquivo JS do jQuery (também via CDN) e o nosso index.js (também previamente criado).

Para que a tela esteja completa no que se refere ao visual, basta que adicionemos o código da Listagem 2 ao arquivo index.css. O mesmo é bem simples e apenas adiciona alguns estilos básicos à página, como uma margem, largura da div container, a linha riscando o meio das palavras para quando um livro for removido da lista, etc.

Listagem 2. Conteúdo do arquivo index.css


.container { 
  width: 40%; 
  margin: 20px auto 0px auto; 
} 
.removido { 
  color: gray; 
}
.removido label {
  text-decoration: line-through; 
} 
ul li { 
  list-style-type: none; 
}

Agora, abra a página HTML em seu navegador e você verá algo semelhante à Figura 1.

Tela inicial com alguns itens adicionados
Figura 1. Tela inicial com alguns itens adicionados

A implementação não está funcional — ainda precisamos de algum código JavaScript/jQuery para adicionar novos livros. Vejamos então como fazer isso adicionando o código da Listagem 3 ao arquivo index.js.

Listagem 3. Conteúdo do arquivo index.js


$(document).ready(function() {
    /** 
     * Add evento de click
     */ 
    function onAdd() {
          var $ul, li, $li, $label, $div, livro; 
          livro = $('.js-novo-livro').val(); 
          
          // valida se “livro” está vazio
          if (livro === '') { 
                 return; 
          }
          
          $ul = $('ul'); 
          $li = $('<li>').appendTo($ul); 
          $div = $('<div>') 
                 .addClass('checkbox') 
                 .appendTo($li);
                 
          $label = $('<label>').appendTo($div); 
          $('<input>') 
                 .attr('type', 'checkbox') 
                 .addClass('js-livro') 
                 .attr('name', 'list') 
                 .click(toggleRemovido) 
                 .appendTo($label);

          $label.append(livro); 
          $('.js-novo-livro').val(''); 
    }
    
    /** 
     * Evento de click do checkbox
     */ 
    function toggleRemovido(ev) {
          var $el; 
          $el = $(ev.currentTarget); 
          $el.closest('li').toggleClass('removido'); 
    }
    $('.js-add').click(onAdd); 
    $('.js-livro').click(toggleRemovido); 
});

Perceba que, novamente, não estamos usando ainda nenhum código do Vue.js, somente JavaScript simples, em conjunto com algumas funções de seleção do jQuery. Vejamos alguns de seus principais pontos:

  • Função ready (linha 1): essa é a função do jQuery responsável por executar o código interno a ela sempre que toda a página tiver terminado de carregar, em alusão ao já conhecido evento onLoad da tag <body>;
  • Função onAdd (linhas 5 a 30): essa função será responsável por lidar com a adição de novos livros à lista diretamente no HTML, usando, para isso, o jQuery e suas funções de manipulação do DOM.
    • Na primeira linha declaramos uma série de variáveis que serão usadas para auxiliar no processo de ler o que o usuário digitou e imprimir em novos elementos da lista;
    • Na linha 6 recuperamos o valor digitado no campo de texto de classe CSS js-novo-livro;
    • Na linha 10 validamos se o valor é igual a vazio. Se for, encerramos a função, evitando, assim, que itens vazios sejam adicionados;
    • Nas linhas 14 a 28, reproduzimos os mesmos elementos de lista HTML que temos nos livros já adicionados, apenas usando seletores e funções do jQuery como appendTo() e addClass();
    • No fim, limpamos o campo de texto para futura digitação (linha 29).
  • Função toggleRemovido (linhas 35 a 39): função que é executada sempre que um clique é feito em qualquer um dos itens da lista. A mesma simplesmente recupera o objeto do elemento correspondente e toggla a classe CSS “removido” ao mesmo. A ação de togglar alguma coisa no jQuery já nos garante o comportamento desejado, isto é, se a classe não existir, ela é adicionada, caso contrário, é removida.

O restante da listagem apenas atrela ambas as funções aos seus respectivos elementos (linhas 40 e 41). Agora, recarregue a página e veja o resultado, adicionando novos livros e checando seus itens.

O exemplo está funcional, todavia, ainda não abraça toda a implementação que pressupomos. Isso porque também precisamos receber o valor do autor do livro e imprimir o mesmo depois do nome do livro. Para isso, algumas poucas alterações se fazem necessárias. Primeiro, altere o seu HTML para incluir um segundo campo de texto, no qual o usuário informará o nome do autor; para isso, remova a div de classe “input-group” e adicione o seguinte trecho de código:


<input placeholder="Adicione um livro" type="text" class="js-novo-livro form-control"> <br>
<input placeholder="Nome do autor" type="text" class="js-novo-autor form-control"> <br>
<button @click="addItem" class="js-add btn btn-primary btn-block" type="button">Adicionar!</button>

Isso substituirá a estrutura Bootstrap que tínhamos, na qual apenas campos com um botão atrelado poderiam ser criados.

Em seguida, vamos modificar a estrutura do nosso JavaScript para reconhecer o novo campo e extrair seu valor apropriadamente. Veja na Listagem 4 o novo conteúdo da função onAdd(), a única que precisará ser alterada.

Listagem 4. Novo conteúdo da função onAdd()


function onAdd() {
    var $ul, li, $li, $label, $div, livro, autor; 
    livro = $('.js-novo-livro').val(); 
    autor = $('.js-novo-autor').val(); 
    
    // valida se “livro” está vazio
    if (livro === '') { 
          return; 
    }
    
    $ul = $('ul'); 
    $li = $('<li>').appendTo($ul); 
    $div = $('<div>') 
          .addClass('checkbox') 
          .appendTo($li);
          
    $label = $('<label>').appendTo($div); 
    $('<input>') 
          .attr('type', 'checkbox') 
          .addClass('js-livro') 
          .attr('name', 'list') 
          .click(toggleRemovido) 
          .appendTo($label);

    $('<big>')
          .appendTo($label)
          .append(livro);
          
    $label.append(" - ");
          
    $('<small>')
          .appendTo($label)
          .append(autor);
          
    $('.js-novo-livro, .js-novo-autor').val('');
}

A primeira alteração que fizemos foi incluir uma nova variável (autor) na declaração inicial do bloco var e, em seguida, extrair seu valor através da função val() do jQuery (linha 4). Todo o conteúdo da criação do checkbox e associação às funções ouvintes não precisaram ser alterados.

Na linha 25 criamos uma nova estrutura, <big>, em alusão ao elemento que envolve o nome do livro. Esse elemento está sendo atrelado ao elemento de label previamente criado, adicionando, assim, o nome do livro como seu conteúdo interno. Na linha 29 adicionamos uma string com o caractere separador e, em seguida, na linha 31, criamos o elemento <small> correspondente ao nome do autor.

Por fim, não esqueça de adicionar a classe do novo campo de texto à função que limpa os campos quando um novo item for adicionado à lista (linha 35). O leitor pode conferir na Figura 2 o resultado da nossa atualização.

Implementação com nome do autor
Figura 2. Implementação com nome do autor

Implementação com o Vue.js

Agora que temos a implementação final e funcional com HTML, JavaScript e CSS, vamos partir para a adaptação do nosso código com o Vue.js. O primeiro passo é importar o arquivo JavaScript do framework em nossa página, tal como:

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>

Lembrando que essa importação deve ser feita antes da do nosso arquivo JavaScript, para que nossas futuras alterações surtam efeito sobre o código do Vue.js. A versão mais recente dada a época de escrita deste artigo é a 2.1.8, mas o leitor pode usar quaisquer versões próximas à data dessa, visto que o core do framework se mantém (vide seção Links para o site oficial do Vue.js).

Para que possamos entender, aos poucos, como o framework funciona, substitua o conteúdo da tag body do arquivo HTML do projeto pelo apresentado na Listagem 5.

Listagem 5. Estrutura HTML para lista de livros com Vue.js


<div id="app" class="container">
   <h2>{{titulo}}</h2>
   <ul>
       <li>{{livros[0]}}</li>
       <li>{{livros[1]}}</li>
   </ul>
</div>

Para testar esse código precisamos mapear os elementos da div container no JavaScript. Em vez de inserir no index.js, vamos injetar manualmente o código da Listagem 6, que, por sua vez, mostra os dados a serem associados às marcações feitas no HTML. Para isso, abra a ferramenta para desenvolvedores do seu navegador (via atalho F12), vá até a aba Console e digite os dois comandos.

Listagem 6. Código JavaScript para alimentar a lista de dados Vue.js


// Comando 1
var data = { 
  livros: ['Orange is The New Black - Piper Kerman', 'A Origem das Espécies - Charles Darwin'], 
  titulo: 'Livros 2' 
};
// Comando 2
new Vue({ 
  el: '#app', 
  data: data 
});

O primeiro trecho de código cria os valores em JSON que usaremos para substituir as marcações em chaves duplas {{}} no HTML. O segundo, new Vue(), instancia um objeto simples do Vue.js através de um seletor (o id da div que servirá como container para os elementos Vue) e dos dados propriamente ditos.

Quando terminar de digitar, você deverá ver no navegador algo como o que temos na Figura 3.

Valores em lista via Vue.js
Figura 3. Valores em lista via Vue.js

Uma boa dica para entender mais a fundo como o framework funciona é analisar a estrutura dos dados antes e depois da atuação do Vue.js. Por exemplo, após a execução de cada um dos comandos anteriores, digite o nome da variável data no mesmo console e verifique o que é impresso, tal como vemos na Figura 4. A primeira imagem demonstra o valor do array antes de sofrer qualquer inicialização pelo Vue.js, o que acontece na segunda imagem. Atente também para os métodos e as referências reativas, bem como os atributos que criamos dentro do array sendo transformados em propriedades do novo objeto data.

Objeto data antes e depois da associação com o Vue.js
Figura 4. Objeto data antes e depois da associação com o Vue.js

Caso não deseje ter de inicializar o objeto do Vue sempre que a página for carregada, adicione os trechos de código ao final do arquivo index.js.

Se tentarmos mudar qualquer uma das propriedades do objeto, a referência será feita diretamente aos elementos HTML. Por exemplo, digite o seguinte trecho de código no console:

data.titulo = "Livros Preferidos";

Após a execução, você verá o título do elemento h2 que criamos ser automaticamente atualizado para o novo texto.

Binding de mão-dupla

Em nosso exemplo, até então, vimos como trazer dados que foram criados como modelos no lado JavaScript da página. Logo, a comunicação esteve restrita a JavaScript -> HTML. Todavia, em muitos casos, precisamos que o contrário aconteça, que os valores digitados nos campos sejam enviados ao JavaScript ou, mais que isso, que a associação ( binding) seja mútua. Para isso, altere o conteúdo do body do arquivo HTML para o demonstrado na Listagem 7.

Listagem 7. Código com campo de input para binding automático


<div id="app" class="container">
   <h2>{{titulo}}</h2>
   <ul>
       <li>{{livros[0]}}</li>
       <li>{{livros[1]}}</li>
   </ul>
   <div class="footer">    
       <hr/> 
       <em>Modifique o título da lista:</em>
       <input v-model="titulo"/>
   </div>    
</div>

Veja que a alteração foi muito simples: tudo que precisamos fazer foi adicionar o atributo v-model a um novo campo de input e o próprio Vue.js se encarrega de fazer o resto. Agora, quando recarregar a página no navegador, sempre que digitar qualquer valor no campo, verá o título da lista ser alterado, além de já termos o campo pré-carregado com o dado atribuído ao titulo no JavaScript.

Nossa próxima alteração consistirá em criar uma forma de iterar sobre uma lista de livros provida pelo JavaScript, de tal forma como tivemos na implementação usando jQuery. O Vue.js oferece um mecanismo interessante para lidar com isso: uma diretiva chamada v-for, que manipula estruturas de arrays ou listas JavaScript. Portanto, nossa lista <ul> pode ser facilmente substituída por:


  <ul>
      <li v-for="livro in livros">{{livro}}</li>
  </ul>

Quando você recarregar a página, verá que não houve nenhuma alteração visual, porém a estrutura agora está mais simples. Se quiser testar a adição de novos itens, abra a ferramenta console do navegador e digite:

data.livros.push("Senhora - José de Alencar");

Você verá a lista ser automaticamente incrementada com o novo valor, tal como temos na Figura 5. O mesmo resultado pode ser alcançado removendo ou modificando itens na mesma lista.

Livro novo inserido via console
Figura 5. Livro novo inserido via console

Com a lista de livros sendo devidamente renderizada na tela, agora podemos partir para a checagem dos já lidos. Para isso, precisaremos mudar um pouco a estrutura de dados que estamos usando, para termos um array mais arrojado, com mais possibilidades. Veja na Listagem 8 o novo código.

Listagem 8. Novo objeto de dados dos livros


var data = { 
  livros: [{ titulo: 'Orange is The New Black', autor: 'Piper Kerman', checked: true },    
          { titulo: 'A Origem das Espécies', autor: 'Charles Darwin', checked: false }], 
  cabecalho: 'Livros Preferidos', 
  novoLivro: '',
  novoAutor: ''
};

Veja que a principal mudança reflete na forma como encapsulamos os dados dos livros, criando um array JavaScript com objetos que nos dá mais poder dinâmico. Agora, em vez de um vetor de strings, temos um vetor de objetos {}, os quais têm propriedades ( titulo, autor e checked) para demarcar um modelo de domínio único e comum entre todos os livros a serem adicionados. Também renomeamos o atributo de título h2 da seção, para evitar confusão com os títulos dos livros. Os atributos novoLivro e novoAutor, por fim, serão usados mais adiante, então não se preocupe com eles por enquanto.

Em contrapartida, nosso HTML precisa sofrer mais algumas alterações para se adequar ao novo modelo de objetos. Veja na Listagem 9 a nova estrutura.

Listagem 9. Novo HTML para contemplar o array de objetos


<div id="app" class="container">
 <h2>{{cabecalho}}</h2>
 <ul>
  <li v-for="livro in livros" v-bind:class="{ 'removido': livro.checked }">
   <div class="checkbox">
    <label>
     <input class="js-livro" name="list" type="checkbox" v-model="livro.checked">
     <big>{{livro.titulo}}</big> - <small>{{livro.autor}}</small>
    </label>
   </div>
  </li>
 </ul>
 <div class="footer">
  <hr/>
  <em>Modifique o título da lista:</em>
  <input v-model="cabecalho"/>
 </div>
</div>

As alterações foram pontuais, mas importantes para o bom funcionamento da página com o Vue.js:

  • Na linha 2 alteramos o nome do atributo de titulo para cabecalho, em alusão ao novo atributo do modelo JavaScript;
  • Na linha 4 temos a mesma estrutura de v-for que já vimos, porém agora com uma nova diretiva: a v-bind. Ela recebe como valor uma estrutura condicional, a qual terá seu valor associado à class CSS do elemento <li>. No caso, quando o valor da propriedade checked de cada livro for true, teremos a string “removido” sendo retornada para a referida classe CSS. Isso nos abstém da obrigação de ter de controlar isso via JavaScript, como vínhamos fazendo;
  • Na linha 7 temos o mesmo campo de checkbox, com as propriedades HTML definidas, e um atributo novo: o v-model, cujo valor será o do atributo checked de cada livro;
  • Na linha 8 acessamos os valores do título e autor de cada livro, via sintaxe de duplas chaves {{}};
  • Na linha 16 alteramos o valor do v-model para cabecalho.

Recarregue a página e confira o resultado da lista pré-carregada com os valores do domínio JavaScript.

Dando continuidade, precisamos adaptar a estrutura do nosso objeto Vue para se adequar ao novo jeito de adicionar livros na listagem HTML. Para isso, altere a construção do referido objeto para a demonstrada na Listagem 10.

Listagem 10. Nova estrutura do objeto Vue


new Vue({ 
   el: '#app', 
   data: data,
   methods: { 
     addLivro: function () { 
       var titulo = this.novoLivro.trim(); 
       var autor = this.novoAutor.trim(); 
       if (titulo && autor) { 
         this.livros.push({ 
           titulo: titulo, 
           autor: autor, 
           checked: false 
         }); 
         this.novoLivro = ''; 
         this.novoAutor = ''; 
       } 
     }
   }
 });

Compare o nosso código JavaScript atual com o do jQuery e observe a expressiva simplificação que tivemos. O jQuery trabalha muito bem com o modelo hierárquico do DOM e consegue manipulá-lo com maestria, porém, quando se trata de aplicações que manipulam modelos de dados baseados no paradigma orientado a objetos, o Vue.js consegue abraçar elegantemente implementações mais enxutas, concisas e organizadas.

No objeto Vue agora temos um atributo methods, que conterá a função addLivro (a ser referenciada no modelo HTML). Essa, por sua vez, faz uso dos atributos novoLivro e novoAutor, que declaramos previamente no objeto de data. Eles servirão para que tenhamos um binding de dados disponível no HTML para o Vue.js, tornando, assim, mais fácil a sua associação com os campos de input. Após recuperar seus respectivos valores, verificamos se ambos estão preenchidos para, então, adicioná-los ao já criado array de livros. No fim, limpamos ambos os atributos para que possam novamente ser usados nos novos campos de texto.

As alterações no HTML serão bem pontuais também, apenas para contemplar o acesso aos novos campos e os triggers de eventos de clique no botão. Veja na Listagem 11 o código que precisamos adicionar antes da nossa lista <ul>.

Listagem 11. HTML com campos de texto e botão de adicionar livro


<input v-model="novoLivro" @keyup.enter="addLivro"
   placeholder="Adicionar novo livro" type="text" 
   class="form-control"> 
<input v-model="novoAutor" @keyup.enter="addLivro"
   placeholder="Adicionar novo autor" type="text" 
   class="form-control"> <br>
 
<span class="input-group-btn"> 
   <button @click="addLivro" class="js-add btn btn-primary btn-block" 
      type="button">Adicionar!</button> 
</span>

O código é bem intuitivo: veja que em ambos os campos de texto adicionamos novamente o atributo v-model, que faz o link bidirecional do valor digitado para os novos atributos criados no objeto data. Além disso, temos uma nova diretiva do Vue.js: a @keyup.enter. Essa diretiva atrela um ouvinte de eventos de keyup (soltura da tecla) para a tecla Enter, especificamente; ou seja, sempre que dermos um enter em qualquer um dos campos de texto, a função addLivro do nosso objeto Vue será chamada. O @ constitui um atalho à diretiva v-on, que adiciona ouvintes de eventos em alusão aos que já temos no DOM. O restante do código não apresenta diferenças significativas em relação ao que criamos antes.

Recarregue a página no navegador e observe o resultado, tal como vemos na Figura 6.

Lista de livros via Vue.js
Figura 6. Lista de livros via Vue.js

Arquitetura MVVM

Nos primeiros exemplos que implementamos no artigo, mostramos como criar instâncias do Vue. Para isso, bastou-nos efetuar uma chamada ao código new Vue({ ... }), o qual recebe um objeto de opções ( options), cujas propriedades consistem no elemento da página onde a instância do Vue será associada, os methods que já vimos e o objeto data com os valores a serem atrelados dentro de cada elemento da nossa View. Neste exemplo, o objeto data é o nosso Model e os elementos DOM onde a instância do Vue será associada constituem nossa View.

Em suma, nosso objeto Vue constitui o meio de campo que o framework faz uso para efetuar o binding (associação) do nosso modelo à nossa view, e vice-versa. Nossa aplicação segue, então, o padrão Model-View-ViewModel (MVVM), onde a instância Vue representa a parte ViewModel desse padrão.

Propriedades e configurações

Digamos que, em nosso modelo de aplicação usando o Vue.js, precisamos que toda vez que uma dada string mude sejam aplicadas algumas transformações a outro dado elemento do DOM. Como aplicaríamos a função ouvinte, digamos, string-mudou? E a quem associaríamos a mesma? Desde já, não temos nenhum recurso do tipo var minhaString='olá'; minhaString.onChange(facaAlgo).

Provavelmente encapsularíamos a setagem e recuperação do valor da string em algum tipo de função que faz algo como, por exemplo, atualizar o DOM cada vez que a string for alterada. Vejamos um rápido demo que trará algumas ideias para nos ajudar. Volte à página da nossa aplicação de lista de livros e, na aba do console, insira o seguinte trecho de código:


var objeto = {};
var texto = '';

Isso apenas criará as declarações de variáveis de que precisaremos para o exemplo. Em seguida, vamos recuperar o elemento h2 de cabeçalho da nossa página:

var h2 = document.getElementsByTagName('h2')[0];

A pergunta é: se atrelarmos a variável texto à propriedade objeto.texto, como poderemos atingir o objetivo de ter o elemento h2 sendo alterado sempre que a propriedade do objeto for alterada também?

Uma opção famosa e bastante usada é a função Object.defineProperty, uma função global do JavaScript que podemos usar livremente para a criação de métodos getter e setter, bem como dos respectivos comportamentos de cada um deles. Vejamos o código da Listagem 12 e a forma como essa função facilmente nos ajuda a configurar ações importantes dentro de nossos objetos, de modo a obter o máximo de comportamento customizado possível.

Listagem 12. Definindo propriedades getter e setter ao objeto


Object.defineProperty(objeto, 'texto', { 
  get: function () { 
    return texto; 
  }, 
  set: function (novoValor) { 
    texto = novoValor;  
    h2.innerHTML = texto;
  } 
});

Para atestar seu funcionamento, basta alterar o valor da propriedade objeto.texto no console e verificar o comportamento do h2 na página. O JavaScript e sua função defineProperty nos asseguram que o innerHTML do h2 será modificado sempre que a propriedade definida sofrer qualquer alteração.

Esse exato mecanismo foi usado pelo Vue.js. Uma vez que os dados são passados para a instância Vue, todas as suas propriedades passam pelo método Object.defineProperty, que atribui getters e setters reativos a eles. Para cada diretiva existente numa página, é adicionado um observador, que será notificado de acordo com o método estabelecido.

Componentes

Aqui está um exemplo de um componente Vue:


// Definindo novo componente chamado button-counter
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">Você clicou em mim {{ count }} vezes.</button>'
})

Componentes reusáveis

Agora que você sabe não só o que a ligação de dados no Vue.js é e como usá-la, mas também como ela funciona, é hora de introduzir outro recurso poderoso do Vue.js. Componentes criados com o Vue.js podem ser usados e reutilizados no aplicativo como tijolos para construir uma casa. Cada componente tem seu próprio escopo de estilos e conexões, sendo completamente isolado dos outros componentes.

A sintaxe para criar componentes é muito semelhante à criação da instância do Vue, bastando, para isso, adicionar o valor extend à mesma:

var MeuComponente = Vue.extend({...})

Isso é necessário para adicionar à variável recém-criada as características de um objeto do tipo Vue. Para entender melhor o contexto da componentização, vamos tentar dividir nosso código da lista de livros em componentes. Como você deve lembrar, nossa listagem consiste essencialmente de três partes: a parte que contém a lista de itens dos livros, a que contém os inputs para adicionar novos itens, e a que permite mudar o título da lista de livros h2.

Vamos alterar nosso código para que ele possa usar três componentes, cada um referente a uma parte específica. Para isso, vamos criar três componentes do Vue.js: ItensComp, AlteraTituloComp e AddItemComp. Todos eles terão a propriedade data retornando o objeto data. O método addLivro passará da instância principal do Vue para o componente AddItemComp. Todo o HTML necessário, por sua vez, partirá da nossa index.html para cada um dos componentes.

Para evitar confusão com nosso código antigo, crie duplicatas da nossa página HTML, bem como do JavaScript. Assim, não perdemos o rastreio do que fizemos até agora. Veja na Listagem 13 o novo conteúdo do nosso JavaScript já componentizado.

Listagem 13. Componentizando nossa listagem de livros.


var data = { 
  livros: [{ titulo: 'Orange is The New Black', autor: 'Piper Kerman', checked: true },    
          { titulo: 'A Origem das Espécies', autor: 'Charles Darwin', checked: false }], 
  cabecalho: 'Livros Preferidos', 
  novoLivro: '',
  novoAutor: ''
};

/** 
 * Declarando os componentes
 */ 
var ItensComp = Vue.extend({ 
  data: function () { 
    return data; 
  }, 
  template: '<ul>' + 
  '           <li v-for="livro in livros" :class="{ \'removido\': livro.checked }">' + 
  '             <div class="checkbox">' + 
  '              <label>' + 
  '                     <input type="checkbox" v-model="livro.checked"> ' + 
  '                     <big>{{ livro.titulo }}</big> - <small>{{ livro.autor }}</small>' + 
  '              </label>' +  
  '             </div>' +  
  '           </li>' + 
  '         </ul>' 
});

var AlteraTituloComp = Vue.extend({ 
  data: function () { 
    return data; 
  }, 
  template: '<input v-model="cabecalho"/>' 
}); 

var AddItemComp = Vue.extend({ 
  data: function () { 
    return data; 
  }, 
  methods: { 
    addLivro: function () { 
      var titulo, autor;
      titulo = this.novoLivro.trim(); 
      autor = this.novoAutor.trim(); 
      if (titulo) { 
        this.livros.push({ 
          titulo: titulo, 
          autor: autor, 
          checked: false 
        }); 
        this.novoLivro = ""; 
        this.novoAutor = ""; 
      } 
    } 
  }, 
  template: 
    '<div>' + 
          '<input v-model="novoLivro" @keyup.enter="addLivro"' +
                 ' placeholder="Adicionar título do livro" type="text" class="form-control" />'  + 
          '<input v-model="novoAutor" @keyup.enter="addLivro"' +
                 ' placeholder="Adicionar autor do livro" type="text" class="form-control" /> <br/>'  + 
          '<span class="input-group-btn">' + 
          '  <button @click="addLivro" class="js-add btn btn-primary btn-block"' +
             ' type="button">Adicionar!</button>'  + 
          '</span>' +
    '<div>'
}); 

/** 
 * Registrando componentes
 */ 
Vue.component('itens-comp', ItensComp); 
Vue.component('altera-titulo-comp', AlteraTituloComp); 
Vue.component('add-item-comp', AddItemComp);

/** 
 * Instanciando o Vue
 */ 
new Vue({ 
  el: '#app', 
  data: data 
});

Essa implementação reaproveita muito do nosso código produzido até então, com uma mescla do jeito Vue.js + jQuery, que vimos anteriormente. Isso é importante para demonstrar a flexibilidade do framework em se adaptar e funcionar bem com diferentes abordagens, abraçando diferentes modelos estruturais, bem como outros frameworks, em sua comunicação interna. Esse poder de flexibilidade confere ao Vue.js características muito importantes no universo de desenvolvimento front-end atual. Vejamos algumas das principais alterações que fizemos:

  • A declaração e instanciação do objeto data (linha 1) mantém-se a mesma, visto que a estrutura dos dados de objeto que estamos usando não mudou do ponto de vista de negócio;
  • Na linha 12 temos agora um novo objeto componente ( ItensComp), que estende, como vimos, de Vue.extend({...}). Como via de regra, agora teremos cada componente com uma função de retorno de dados (linha 13), que simplesmente retornará o objeto data definido previamente. Mesmo sendo igual para os três componentes que criaremos, esse tipo de abordagem flexibiliza a forma como podemos customizar tal objeto, possibilitando ter dados específicos para cada componente. Em seguida, na linha 16, temos a definição do template HTML + Vue.js binding que será usado pelo framework para construir a View desse componente. Aqui, reaproveitamos toda a estrutura já definida no HTML, que deixará de existir lá;
  • O componente AlteraTituloComp (linha 28) se encarregará de definir os dados e o template para o campo de input que altera o texto do cabeçalho da nossa página, tal como já tínhamos antes;
  • O componente AddItemComp (linha 35) define a estrutura principal de cadastro da nossa lista de livros, com mais métodos que os outros dois anteriores:
    • Método data: faz o mesmo que os anteriores;
    • Método methods: define o método que o componente possibilitará ser chamado via modelo internamente: addLivro, no caso;
    • Método template: faz o mesmo que os anteriores. Atente para a estrutura de concatenação do texto (tudo em string) referente ao HTML do componente. A mescla entre aspas simples e duplas pode confundir as vezes, bem como as quebras de linhas entre elas. Cuidado quando for implementá-los.
  • As linhas 71 a 73 trazem a associação definitiva dos objetos JavaScript de componentes aos seus respectivos nomes. Isso será importante para que possamos fazer o binding deles na página HTML;
  • Finalmente, a linha 78 traz a instanciação do objeto Vue, que, de fato, é quem liga todos os pontos de toda a implementação.

Para que possamos ver o JavaScript interpretando todo o aparato de implementação que fizemos, algumas alterações no HTML se fazem necessárias. Agora, nossa div de container será simplificada e conterá apenas o conteúdo mostrado na Listagem 14.

Listagem 14. Adicionando as tags dos novos componentes.


<div id="app" class="container">
   <h2>{{cabecalho}}</h2>
    <add-item-comp></add-item-comp>
    <itens-comp></itens-comp>
    <div class="footer">
          <hr/>
          <em>Mude o título da lista aqui:</em>
          <altera-titulo-comp></altera-titulo-comp>
    </div>
</div>

Veja como fica simples trabalhar com o conceito de tags. Elas encapsulam toda a lógica definida no JavaScript (que também traz os templates) e demarcam o local onde cada componente deve ser inserido. Dessa forma, conseguimos de fato abraçar o conceito de componentização, uma vez que tais estruturas podem ser reutilizadas em várias partes da sua aplicação, sem a necessidade de duplicar trechos de código e/ou HTML. Veja na Figura 7 o resultado da nossa reestruturação.

Resultado final da aplicação com tags Vue.js
Figura 7. Resultado final da aplicação com tags Vue.js

A extensibilidade do Vue.js vai muito além do uso de tags, diretivas, ou da própria componentização. Ele abraça tão bem o universo de integração JavaScript-HTML que aparenta ter nascido e crescido junto a eles. Mais que isso, ele entende muito bem como ambos se comunicam e faz seu papel de facilitar a tradução dessa conversa, não só para ambos os lados da arquitetura, mas principalmente para os próprios desenvolvedores.

Há muito o que fazer com o Vue.js e as possibilidades são praticamente infinitas, já que ele se adequá muito bem a tudo. Muitos desenvolvedores iniciantes se questionam sobre o poder de integração do Vue.js à frameworks famosos, como o AngularJS, por exemplo, que usa uma sintaxe parecida com a dele. Nesse ponto, tome cuidado com a incompatibilidade entre versões ou até mesmo com o conflito de operadores, estruturas e condicionadores usados em ambos os frameworks, uma vez que ambos têm propósitos já bem pré-estabelecidos. Nessas horas, uma boa consulta à documentação oficial (vide seção Links) ajuda bastante e pode lhe trazer ricos insumos para a construção de um conhecimento maior acerca do framework, o que vale não somente do Vue.js, mas também para qualquer outro que você, leitor, venha a utilizar.

Links Úteis

Saiba mais sobre Vue.js ;)

  • Vue.js: Utilizando data bind unidirecional e bidirecional
    Neste artigo conheceremos o Vue.js, um framework que tem por objetivo otimizar a construção das views de uma aplicação, utilizando para isso a ligação entre o código HTML e o JavaScript.
  • Vue.js: Diretivas condicionais
    Neste artigo veremos como utilizar as diretivas v-if e v-show do framework Vue.js para exibir um determinado conteúdo apenas se uma condição for atendida.
  • O que é Vue.js?
    Neste curso aprenderemos o que é o Vue.js, um dos frameworks JavaScript que mais vem crescendo nos últimos anos.