SPA refere-se a uma aplicação web de página única. Neste modelo, o navegador carrega uma única página HTML e atualiza dinamicamente o conteúdo conforme o usuário interage, sem recarregar a página inteira. A navegação é gerenciada pelo lado do cliente, frequentemente utilizando o fragmento de identificador na URL (após o símbolo #), que não dispara uma recarga do navegador.
Por exemplo, uma URL típica de uma aplicação Vue em desenvolvimento pode ser:
http://localhost:8080/#/configuracao/tarefa
A parte após # (o fragmento) define a rota e o componente a ser exibido.
APIs RESTful
As interfaces RESTful são comuns em projetos modernos. Elas mapeiam operações CRUD (Criar, Ler, Atualizar, Deletar) para métodos HTTP específicos:
- POST para inserir novos dados.
- GET para recuperar dados.
- PUT para atualizar dados existentes.
- DELETE para remover dados.
Por exemplo, considerando o endpoint /api/v1/projetos:
GET /api/v1/projetosretorna uma lista de projetos.POST /api/v1/projetoscria um novo projeto.GET /api/v1/projetos/123obtém o projeto com ID 123.PUT /api/v1/projetos/123atualiza o projeto 123.DELETE /api/v1/projetos/123exclui o projeto 123.
Essa abordagem simplifica a definição de endpoints.
Configurando o Ambiente de Desenvolvimento
Instalação do Node.js
Baixe e instale o Node.js do site oficial. Após a instalação, verifique com os comandos:
node -v
npm -v
Se versões forem exibidas, o ambiente está configurado.
Instalação do Vue CLI
O Vue CLI é uma ferramenta para projetos Vue. Instale globalmente:
npm install -g @vue/cli
Crie um novo projeto usando o template webpack:
vue create meu-app
Selecione as opções desejadas durante a configuração. Após a conclusão, entre no diretório e inicie o servidor de desenvolvimento:
cd meu-app
npm run serve
A aplicação estará disponível em http://localhost:8080.
Padrões de Código
Considere adotar um estilo como ESLint com regras específicas, como:
- Indentação com dois espaços.
- Uso de aspas simples.
- Avoidar variáveis redundantes.
- Ponto e vírgula opcional, dependendo da configuração.
Estrutura do Projeto
Uma estrutura típica após scaffolding inclui:
├── src
│ ├── assets
│ ├── components
│ ├── views
│ ├── router
│ ├── store
│ ├── services
│ ├── App.vue
│ └── main.js
├── public
├── package.json
└── vue.config.js
Organize componentes reutilizáveis em components, páginas em views, e serviços de API em services.
Configurando o Roteamento
Edite src/router/index.js para definir rotas:
import Vue from 'vue'
import VueRouter from 'vue-router'
import PaginaInicial from '../views/PaginaInicial.vue'
import Detalhes from '../views/Detalhes.vue'
Vue.use(VueRouter)
const rotas = [
{
path: '/',
name: 'PaginaInicial',
component: PaginaInicial
},
{
path: '/detalhes/:identificador',
name: 'Detalhes',
component: Detalhes
}
]
export default new VueRouter({
routes: rotas
})
O parâmetro :identificador permite rotas dinâmicas.
Integrando uma API com Axios
Instale o Axios:
npm install axios
Crie um serviço de API em src/services/api.js:
import axios from 'axios'
const clienteHttp = axios.create({
baseURL: '/api/v1',
timeout: 5000
})
export default {
obterRecursos(endpoint) {
return clienteHttp.get(endpoint)
},
enviarDados(endpoint, dados) {
return clienteHttp.post(endpoint, dados)
},
atualizarRecurso(endpoint, dados) {
return clienteHttp.put(endpoint, dados)
},
removerRecurso(endpoint) {
return clienteHttp.delete(endpoint)
}
}
No main.js, torne o serviço acessível globalmente:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import apiService from './services/api'
Vue.prototype.$api = apiService
new Vue({
router,
render: h => h(App)
}).$mount('#app')
Proxy para APIs em Desenvolvimento
Para evitar problemas de CORS, configure o proxy no vue.config.js:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
}
}
Isso redireciona requisições /api para um servidor backend local.
Componentes Vue
Cada arquivo .vue contém template, script e estilo:
<template>
<div class="componente-exemplo">
<h2>{{ titulo }}</h2>
<button @click="incrementar">Contagem: {{ contagem }}</button>
</div>
</template>
<script>
export default {
data() {
return {
titulo: 'Componente de Exemplo',
contagem: 0
}
},
methods: {
incrementar() {
this.contagem++
}
}
}
</script>
<style scoped>
.componente-exemplo {
margin: 1rem;
padding: 1rem;
border: 1px solid #ccc;
}
</style>
Renderizando Dados de uma API
No componente PaginaInicial.vue, carregue e exiba dados:
<template>
<div>
<Cabecalho></Cabecalho>
<ul>
<li v-for="item in listaItens" :key="item.id">
<router-link :to="`/detalhes/${item.id}`">
{{ item.nome }}
</router-link>
<span>{{ formatarData(item.dataCriacao) }}</span>
</li>
</ul>
<Rodape></Rodape>
</div>
</template>
<script>
import Cabecalho from '../components/Cabecalho.vue'
import Rodape from '../components/Rodape.vue'
import { formatarData } from '../utils/data'
export default {
components: { Cabecalho, Rodape },
data() {
return {
listaItens: []
}
},
async created() {
try {
const resposta = await this.$api.obterRecursos('projetos')
this.listaItens = resposta.data
} catch (erro) {
console.error('Falha ao carregar projetos:', erro)
}
},
methods: {
formatarData
}
}
</script>
Para formatar datas, crie um utilitário em src/utils/data.js:
export function formatarData(dataString) {
const data = new Date(dataString)
return data.toLocaleDateString('pt-BR')
}
Página de Detalhes
No componente Detalhes.vue, exiba informações detalhadas:
<template>
<div v-if="detalhesItem">
<Cabecalho></Cabecalho>
<h1>{{ detalhesItem.titulo }}</h1>
<p>Criado por: {{ detalhesItem.autor }} em {{ formatarData(detalhesItem.dataCriacao) }}</p>
<div v-html="detalhesItem.conteudo"></div>
<Rodape></Rodape>
</div>
</template>
<script>
import Cabecalho from '../components/Cabecalho.vue'
import Rodape from '../components/Rodape.vue'
import { formatarData } from '../utils/data'
export default {
components: { Cabecalho, Rodape },
data() {
return {
detalhesItem: null
}
},
async created() {
const id = this.$route.params.identificador
try {
const resposta = await this.$api.obterRecursos(`projetos/${id}`)
this.detalhesItem = resposta.data
} catch (erro) {
console.error('Falha ao carregar detalhes:', erro)
}
},
methods: {
formatarData
}
}
</script>