Javascript

O que é uma SPA: Single-Page Application?

Por em

Revolução! Essa é a palavra que define o quanto o front-end tem crescido nos últimos anos e o SPA (Single-Page Application) é o ápice disso.

Se você ainda não “brincou” com SPA saiba que está perdendo muito, a técnica é o acrônimo de Single-Page Application, ou aplicação de página única em tradução livre e é construída, básicamente, com HTML, CSS e Javascript. A técnica é a base para se criar aplicações híbridas que funcionam tando no navegador quanto em dispositivos mobile e desktop de forma “nativa”, quero dizer, baixando da store (Google Play e App Store, por exemplo) ou instalando no seu sistema operacional favorito.

Existem muitas formas de criar um SPA, frameworks como Angular, Vue.js e React oferecem excelentes propostas, com sistema de rotas, estado dos dados (Single source of truth), sistema de templates, mas eu estou me adiantando, vamos com calma.

O que de fato é um SPA?

Um SPA é uma aplicação web que roda em uma única página, se assemelhando a um aplicativo desktop ou um mobile, são leigamente chamadas de “páginas ajax”, um bom exemplo que gosto de usar é o Gmail do Google, ele é um SPA, a navegação na aplicação rola toda em uma única página e todo o conteúdo é carregado de uma vez ou obtido dinâmicamente (ou seja, via requisições Ajax).

A aplicação SPA pode ser construída de diversas formas, a mais comum é ser auxiliado por um framework Javascript como Angular ou Vue.js, ambos possuem sistemas de rotas e clientes HTTP para fazer requisições a recursos externos (uma API, por exemplo).

Aqui na School of Net temos cursos de SPA construído com frameworks que vão agilizar MUITO o seu dia a dia:

Curso de Vue.js (Gratuito*)
Curso de Angular (Gratuito*)
Curso de Ember.js

Você não precisa aprender todos, o ideal é escolher um e se especializar, essa decisão eu deixo pra você, mas adianto que todos são excelentes e a decisão sobre qual escolher pode partir de um gosto particular ou até sobre seu ambiente (a empresa que trabalha, por exemplo), minha recomendação é você conhecer dois ou três e decidir entre eles. O SPA virá naturalmente.

Mas você pode fazer algo mais “puro”, digo, apenas Javascript. Rescentemente eu desenvolvi um HD Virtual sem nenhum framework, com código modularizado, persistência dos dados no Firebase (assim você não precisa desenvolver um back-end, ou seja, algum local para salvar as informações online) e até mesmo permissões de acesso, foi bem legal e o melhor é que o curso está disponível aqui na School of Net também.

Criando um hd virtual com PWA e Firebase

Criando seu primeiro SPA

Agora, que tal um pouco de prática? Vamos criar nosso primeiro SPA?

Nós vamos reproduzir este efeito:

https://codepen.io/erikfig/full/gKLqOg/

Este exemplo usa o Vue.js, simples e com uma curva de aprendizado muito curta, você não precisa conhecer para acompanhar este passo a passo e quero esclarecer que o foco do artigo é SPA, o Vue é uma ferramenta que vamos usar para alcançar nossos objetivos.

Em primeiro lugar vamos precisar de um template HTML 5, eu preparei algo bem simples, incluí 3 comentários informando aonde inserir o HTML, o CSS e o Javascript:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Primeiro SPA</title>
  <!--a aqui vai o css -->
</head>

<body>
    <!--a aqui vai o html -->

    <!--a aqui vai o javascrip -->
  </body>
</html>

Eu espero que aqui não tenha nenhuma novidade para você, mas sempre existem os comentários.

Agora precisamos carregar o javascript do Vue.js, é ele que vai fornecer tudo o que precisamos para nosso SPA funcionar:

Insira na sessão destinada ao Javascript

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

E vamos incluir o HTML da nossa página:

<div id="app">
  <h1>Hello SPA!</h1>
  <nav>
    <router-link to="/">Home</router-link>
    <router-link to="/sobre">Sobre</router-link>
    <router-link to="/contato">Contato</router-link>
  </nav>
  <router-view></router-view>
</div>

As novidades aqui são as tags router-link e a router-view, elas serão tratadas pelo VueRouter insirido anteriormente, o router-link será transformado em dats de link (tag a) e ao clicar neste links um HTML que ainda vamos definir no próximo passo será renderizado aonde vemos a tag router-view. Também gostaria de uma atenção na primeira tag, o div com id app, este será nosso seleletor para o Vue, ou seja, o Vue.js vai assumir que deve “gerenciar” tudo dentro desta tag.

Note que com estas informações você pode incrementar seu código da forma que você quiser, é só HTML, da pra inserir classes na tag que quiser, incluindo a tag router-link quedevpois leva todos os atributos associados a ela para a tag a. A tag router-view deixa de existir quando o conteúdo é acrescentado, então não adianta tentar acrescentar atributos nela.

Agora vamos acrescentar alguma ação ao nosso app:

var home = {template: '<div>home</div>'}
var sobre = {template: '<div>sobre</div>'}
var erro = {template: '<div>erro</div>'}

var routes = [
  { path: '/', component: home },
  { path: '/sobre', component: sobre },
  { path: '*', component: erro }
]

var router = new VueRouter({
  routes
})

var app = new Vue({
  el: '#app',
  router
})

Veja como o código é simples, mas ao mesmo tempo poderoso. Eu usei o var propositalmente para deixar mais simples, se quiser saber mais sobre práticas modernas de criação de variáveis consulte nosso direto ao ponto Scope e Hoisting no Javascript.

Nas primeiras linhas eu simplesmente criei 3 variáveis e associei um objeto com um item chamado template, o valor o item template será o que será mostrado no lugar da tag router-view, simples assim, você pode enriquecer como quiser, na verdade eu deixei pequeno para ficar simples de ler, mas no exemplo final que mostrei acima os valores são estes a baixo, mas apenas para referência, lembre-se, você pode colocar o HTML que quiser.

var home = {template: '<div><h3>Página inicial</h3><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ligula massa, fermentum gravida congue in, facilisis a purus. Nullam lobortis, velit a rutrum ultrices, mauris ipsum posuere turpis, ut porttitor mauris tellus nec urna. In hac habitasse platea dictumst. Pellentesque sit amet metus in nunc imperdiet rhoncus. Proin risus augue, pulvinar et pellentesque ac, gravida sed metus. Etiam ac erat a risus interdum commodo non sit amet massa. In sed felis rhoncus, consequat ex ac, viverra ipsum. Suspendisse mi eros, interdum et porta ac, pharetra non nisi. Curabitur auctor ornare massa eu pretium.</p></div>'}
var sobre = {template: '<div><h3>Sobre SPA</h3><p>Duis viverra, turpis at accumsan commodo, urna leo scelerisque sem, ut vulputate lacus leo at dui. Morbi et enim in massa volutpat pharetra at eu purus. Cras ut congue erat. Suspendisse ac hendrerit dui. Donec id eros vel dolor dignissim suscipit. In ac enim in nisl gravida venenatis a in lectus. Donec posuere metus in purus consequat lobortis. Aliquam consectetur, nibh eu rutrum efficitur, sem turpis sodales diam, eget molestie nisl ex ac metus. Nullam ligula ante, commodo efficitur nisl sed, cursus fermentum nisl.</p></div>'}
var erro = {template: '<div><h3>Página não encontrada</h3><p>:(</p></div>'}

Agora precisamos de uma forma de referenciar a url do router-link para estas 3 varáveis (home, sobre e erro), então um array de objetos resolveu pra gente, apenas informei qual a url usando o item path e qual html deve ser referenciado usando o item component e armazenei tudo na variável routes.

Restão dois blocos de código, da variável router e da variável app.

Na variável router eu iniciei o VueRouter e informei a minha lista de rotas e variáveis com template (são chamados componentes no Vue.js) que criei anteriormente, note que o routes que fica na linha 12 é um atalho para “routes: routes” (sem aspas).

Finalmente eu iniciei o Vue.js, informei no item el que ele deve renderizar o meu aplicativo no item com id app e passei o VueRouter configurado nas linhas anteriores para ele, noamente o router é um atalho para “router: router” (sem aspas).

Note que eu tenho o router e o routes, a apalavra com o er no final identifica o cara que faz aquilo, ou seja, o criador e manipulador das rotas e a palavra em s no final é o plural de route, ou seja “rotas”, é interessante conhecer esta diferença para evitar confundir os dois nomes.

Neste ponto, se tudo estiver certo você deve conseguir rodar a página no navegador (use um servidor web, como Apache, por exemplo)  e navegar, note que o link contato vai exibir o erro, porque este link não foi configurado, e temos uma rota * (asterisco adiz que qualquer rota renderiza o componente erro, como o VueRouter le as rotas de cima para baixo, caso não esteja na lista ela será executada por último quando não existem mais rotas).

Se quiser deixar bonitinho como o meu, eu inclui algum css e transições, você vai precisar alterar seu HTML incluindo a tag transition e eu inclui um div com classe transition para servir de “container” para as animações, eu mesmo criei esse div e configurei o css que vou mostrar mais pra frente, por hora, o HTML alterado:

<div id="app">
  <h1>Hello SPA!</h1>
  <nav>
    <router-link to="/">Home</router-link>
    <router-link to="/sobre">Sobre</router-link>
    <router-link to="/contato">Contato</router-link>
  </nav>
  <div class="transition">
    <transition
                enter-active-class="animated fadeIn"
                leave-active-class="animated fadeOutRight">
      <router-view></router-view>
    </transition>
  </div>
</div>

O enter-active-class informa a classe css a ser com animação a ser executada no momento em que a página “entra”, o leave-active-class informa a classe css a ser executada quando a página “sai

As classes css que usei (animated, fadeIn e fadeOutRight) são do Animated.css, assim nós ganhamos um tempo com estas animações.

Para incluir o Animated.css carregue com a tag link no Header, aonde está o comentário informando a área reservada para o css:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.2/animate.min.css">

Com isso ele já deve mostrar alguma animação, mas as coisas estão bem feias e deslocadas, adicione este CSS para deixar “bonito” o nosso SPA logo após o Animate.css

<style>
@import url('https://fonts.googleapis.com/css?family=Lato|Sunflower:700');

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

html, body, #app {
  height: 100%;
}

#app {
  background: #74b9ff;
  padding: 30px;
  font-family: 'Lato', sans-serif;
  overflow: hidden;
}

.transition {
  position: relative;
}

.animated {
  position: absolute;
  top: 0;
}

h1 {
  margin-top: 0;
  font-family: 'Sunflower', sans-serif;
  color: #fff;
}

nav {
  background: #0984e3;
  padding: 10px;
}

nav a {
  display: inline-block;
  text-decoration: none;
  color: #fff;
  background: #e17055;
  padding: 10px;
}

nav a:hover {
  background: #d63031;
}
</style>

Agora sim, nossa página deve estar bem bacana.

Note que com pouquíssimas linhas de código eu criei um SPA completo, ele carrega toda a aplicação de uma vez e depois disso o usuário não precisa mais ver o refresh da página.

Vale lembrar que este é um exemplo simples, eu já criei um painel de gerênciamento de site completo com Vue.js, melhor ainda, dentro do Electron, no final eu faço o build para Windows com instalador (sim, um aplicativo Windows, .exe, com instalador, no menu Iniciar, desinstalador, tudo o que tem direito).

Aqui para ver o curso de CMS com Vue.js e Electron adiministrando um site escrito em PHP.

Você também pode ver nossos planos de estudo, tem um de Vue.js ue vai MUITO além do que vimos aqui e vemos Vuex, build ES6 para ES5, usamos o WebPack, Vue Cli, tem muita coisa pra aprender.

Plano de estudos com Vue.js

É isso, espero que tenha sido útil e bons estudos!