Wilson Tayar //@wilsontayar

Gerenciamento de rotas na URL em Single Page Apps (SPA)

Uma Single Page Application, também conhecida por SPA, possui algumas características bem distintas se a compararmos com outros web apps que estamos acostumados a desenvolver.
Sua principal característica é o fato de ser uma única página que carrega outros conteúdos dinamicamente com chamadas AJAX, enquanto que o mais comum é encontrarmos aplicativos que solicitam páginas inteiras ao servidor, ou seja, com <head>, <body> e os demais elementos que a compõem.

Uma prática que vem se tornando cada vez mais comum é o uso de URLs com hash ("#") para "marcar" o conteúdo que foi carregado na Single Page App. Ao utilizarmos esta prática facilitamos a navegação do usuário, pois possibilitamos que o botão voltar do navegador funcione, além de deixarmos a app funcionando como se fosse um site comum. Para quem quer um exemplo, vale gastar alguns minutos navegando no Grooveshark e prestar atenção nas URLs com "#!" (hashbang). Vale dizer também que apesar do uso da "#" ser uma prática comum ela não é obrigatória, ou seja, você também pode fazer sua SPA com rotas normais (/rota, por exemplo).

Neste post, pretendo mostrar duas libraries em javascript que já utilizei em alguns projetos para o gerenciamento e funcionamento das rotas de uma SPA. No final do post também listei alguns nomes muito bons para ajudar quem está procurando outras alternativas.

Sammy.js

O Sammy.js é, como a própria descrição de seu repo no github diz:

"um framework pequeno construído sobre o jQuery e inspirado no Sinatra do Ruby".

Ele se destaca por ser muito simples e ter diversas funcionalidades muito úteis. Aqui estão algumas delas:

  • Criar rotas utilizando regular expression
  • Rotas com parâmetros
  • Vincular funções específicas para certas rotas
  • Carregar e criar templates
  • Controle de sessão próprio

Para utilizá-lo é simples, como mostrado no código abaixo, retirado da página de introdução do framework:

// define a new Sammy.Application bound to the #main element selector
Sammy('#main', function() {

  // define a 'get' route that will be triggered at '#/path'
  this.get('#/path', function() {
    // this context is a Sammy.EventContext
    this.$element() // $('#main')
        .html('A new route!');
  });
}).run();

Basta indicarmos em qual container serão carregadas todas as rotas (#main no exemplo acima) e logo depois passarmos uma rota ("#/path") e uma função que será executada sempre que ele reconhecer a rota na URL.

Vocês podem conferir um tutorial muito bom sobre diversas funcionalidades do Sammy.js no site oficial: http://sammyjs.org/docs#tutorials.

Como dica, a inicialização das rotas do Sammy pode ficar um pouco "macarrônico" dependendo da estrutura e complexidade da sua app. Tente sempre manter o funcionamento das rotas o mais simples possível chamando initializers de outras funções para posicionar tudo em sua página.

Pagerjs

O Pagerjs é uma library baseada em jQuery e knockout. Com uma página de demonstração que deixa qualquer um com vontade de usá-la alguma vez na vida, ela possui diversas features que facilitam a vida do desenvolvedor na hora de fazer mapping entre suas view models. Acredito que o pagerjs seja a melhor escolha para quem já utiliza o knockout (confiram o repo do github aqui).

Vamos analisar juntos o código abaixo, retirado de alguns trechos da página de demonstração:

<html>
<body>
<div class="container" style="padding-top: 30px;">

    <!-- #!/first-page -->
    <div data-bind="page: {id: 'first-page'}">

        <!-- #!/first-page/details -->
        <div data-bind="page: {id: 'details'}">

        </div>

    </div>

    <!-- #!/another-page -->
    <div data-bind="page: {id: 'another-page'}">

    </div>
</div>
</body>
</html>
  // use #!/ instead of the default #
  pager.Href.hash = '#!/';

  // extend your view-model with pager.js specific data
  pager.extendWithPage(viewModel);

  // apply the view-model using KnockoutJS as normal
  ko.applyBindings(viewModel);

  // start pager.js
  pager.start();

Basicamente, o Pagerjs se junta à sua view model (através do extendWithPage) e cria algumas propriedades dele. Desta forma, você consegue fazer o bind via knockout das rotas para containers da sua página.

São tantas as funcionalidades do Pagerjs que fica difícil escrever um pouco sobre todas elas. Convido os interessados a explorar melhor a página de demonstração e sentir na pele a facilidade que os bindings trazem.

A diferença básica do pagerjs para o sammy.js é que o sammy mapeia a sua rota para funções que tem o trabalho de carregar as views, enquanto que com o pagerjs, suas rotas estão ligadas com bindings ao seu HTML. Não podemos esquecer que o Pagerjs foi criado para ser uma extensão do uso normal do knockout. Caso você estiver lidando com projetos pequenos, mantenha tudo simples e não crie dependências de frameworks inteiros apenas para ter a flexibilidade das rotas. Para isso existem outras libraries que não dependem de frameworks.

Outras opções

Para quem está procurando gerenciar suas rotas, aqui estão mais algumas boas alternativas:

  • Crossroads.js - criada pelo brasileiro Miller Medeiros é uma ótima escolha para qualquer projeto, já que não depende de nenhum framework. Sua única dependência é do JS-Signals, também desenvolvido por ele.
  • pathjs - extremamente pequena, também não depende de nenhum framework. Já não é atualizada no github há algum tempo.
  • Davis.js - muito parecida com o sammy.js, já que também é baseada no Sinatra. Depende do jQuery para que tudo funcione.

 

Abraços!

Wilson Tayar