Configuração Essencial do Frontend Vue.js
Ao iniciar um novo projeto Vue.js, é fundamental realizar algumas configurações iniciais para garantir um ambiente de desenvolvimento limpo e eficiente. Os módulos de terceiros devem ser instalados via um gerenciador de pacotes como cnpm ou npm, e a verificação no arquivo package.json é uma boa prática para confirmar a instalação. É importante lembrar que projetos Vue.js, focados no frontend, não requerem ambientes virtuais como os projetos Python.
Estruturação do Projeto Vue.js
Para manter o projeto organizado e livre de excessos, é recomendado limpar os arquivos boilerplate gerados automaticamente:
Componente Raiz: App.vue
Remova quaisquer estilos ou elementos desnecessários, mantendo apenas o ponto de entrada para as rotas.
<template>
<div id="app-container">
<router-view/>
</div>
</template>
<script>
export default {
name: 'AppRoot',
};
</script>
<style>
/* Estilos globais ou reset podem ser importados aqui */
#app-container {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
</style>
Configuração de Rotas: router/index.js
Mantenha apenas as rotas essenciais para o início do projeto, removendo exemplos ou rotas não utilizadas.
import Vue from 'vue';
import VueRouter from 'vue-router';
import LandingPage from '../views/LandingPage.vue';
Vue.use(VueRouter);
const routes = [
{
path: '/',
name: 'landing',
component: LandingPage,
},
];
const router = new VueRouter({
mode: 'history', // Ou 'hash'
base: process.env.BASE_URL,
routes,
});
export default router;
Página de Exemplo: LandingPage.vue (ou similar)
Simplifique os componentes de página para conter apenas o conteúdo básico necessário.
<template>
<div class="landing-page">
<h1>Bem-vindo à Página Inicial</h1>
</div>
</template>
<script>
export default {
name: 'LandingPage',
};
</script>
<style scoped>
.landing-page {
text-align: center;
padding: 20px;
}
</style>
Estilos CSS Globais
É uma prática comum inicializar os estilos de um projeto web com um "reset" CSS, removendo margens e preenchimentos padrão dos navegadores para uma maior consistência visual.
- Crie um arquivo
global.cssdentro de uma pastaassets/css.
/* Arquivo: src/assets/css/global.css */
/* Define estilos globais e reinicia as propriedades padrão do navegador para elementos HTML comuns */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
box-sizing: border-box; /* Adicionado para um modelo de caixa mais intuitivo */
}
/* Reset específico para alguns elementos */
body {
line-height: 1;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #333;
}
ol, ul {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
table {
border-collapse: collapse; /* Unifica bordas de tabelas */
border-spacing: 0;
}
input, select, textarea, button {
font-family: inherit;
font-size: inherit;
}
- Importe este arquivo no
main.jspara que os estilos sejam aplicados globalmente.
// Arquivo: src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
// Importa os estilos CSS globais
import '@/assets/css/global.css';
Vue.config.productionTip = false;
new Vue({
router,
render: h => h(App),
}).$mount('#app-container');
Configurações JavaScript Globais
Para facilitar a manutenção de URLs de API e outras configurações que podem mudar entre ambientes (desenvolvimento, produção), é útil centralizá-las em um arquivo JavaScript global.
- Crie um arquivo
api_settings.jsdentro deassets/js.
// Arquivo: src/assets/js/api_settings.js
export default {
BASE_API_URL: 'http://127.0.0.1:8000/api/v1', // URL base da API
APP_NAME: 'Meu Projeto Web'
// Outras configurações podem ser adicionadas aqui
};
- Importe este arquivo em
main.jse o adicione ao protótipo do Vue para acessá-lo facilmente em qualquer componente.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
// ... outras importações ...
import apiSettings from '@/assets/js/api_settings';
// Adiciona as configurações da API ao protótipo do Vue
Vue.prototype.$api = apiSettings;
new Vue({
router,
render: h => h(App),
}).$mount('#app-container');
- Acesse as configurações em qualquer componente Vue.
// Exemplo de uso em um componente Vue
export default {
methods: {
fetchData() {
// Usando o Axios (ainda a ser instalado)
this.$axios.get(`${this.$api.BASE_API_URL}/produtos`).then(response => {
console.log(response.data);
}).catch(error => {
console.error('Erro ao buscar dados:', error);
});
}
}
};
Instalação e Configuração de Módulos Essenciais
Integração com Axios para Requisições HTTP
Axios é uma biblioteca popular para fazer requisições HTTP, amplamente utilizada em projetos Vue.js.
- Instale o Axios:
cnpm install axios -S - Configure-o globalmente no
main.js.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
import axios from 'axios';
// ... outras importações ...
Vue.prototype.$axios = axios;
// Opcional: Configurações padrão para o Axios
axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.withCredentials = true; // Para enviar cookies automaticamente
new Vue({
// ...
}).$mount('#app-container');
- Utilize o Axios em seus componentes.
// Exemplo em um componente Vue
export default {
methods: {
async loginUser(credentials) {
try {
const response = await this.$axios.post(`${this.$api.BASE_API_URL}/auth/login`, credentials);
console.log('Login bem-sucedido:', response.data);
// Salvar token, redirecionar, etc.
} catch (error) {
console.error('Falha no login:', error.response ? error.response.data : error.message);
}
}
}
};
Gerenciamento de Cookies com Vue-Cookies
Para armazenar tokens de autenticação ou outras informações no navegador, vue-cookies é uma solução prática.
- Instale
vue-cookies:cnpm install vue-cookies -S - Configure-o globalmente no
main.js.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
import VueCookies from 'vue-cookies';
// ... outras importações ...
Vue.use(VueCookies); // Para Vue 2
// Ou, se precisar de acesso direto ao objeto:
// Vue.prototype.$cookies = VueCookies;
new Vue({
// ...
}).$mount('#app-container');
- Use
vue-cookiesem seus componentes.
// Exemplo em um componente Vue
export default {
methods: {
saveAuthToken(token) {
this.$cookies.set('authToken', token, '1d'); // Salva o token por 1 dia
},
getAuthToken() {
return this.$cookies.get('authToken');
},
removeAuthToken() {
this.$cookies.remove('authToken');
}
}
};
Integração da Biblioteca de Componentes Element UI
Element UI oferece um conjunto rico de componentes de interface do usuário, acelerando o desenvolvimento frontend.
- Instale o Element UI:
cnpm install element-ui -S - Configure-o no
main.js.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css'; // Importa o tema CSS
// ... outras importações ...
Vue.use(ElementUI);
new Vue({
// ...
}).$mount('#app-container');
- Agora você pode usar os componentes do Element UI em seus modelos.
<template>
<div>
<el-button type="primary" @click="handleClick">Meu Botão</el-button>
<el-alert
title="Sucesso"
type="success"
description="Operação realizada com êxito.">
</el-alert>
</div>
</template>
<script>
export default {
methods: {
handleClick() {
this.$message('Botão clicado!');
}
}
};
</script>
Configurando jQuery e Bootstrap (para projetos legados ou específicos)
Embora Vue.js e Element UI forneçam soluções modernas, pode ser necessário integrar jQuery e Bootstrap (especialmente a versão 3) para compatibilidade com sistemas existentes ou requisitos específicos. Este processo requer configuração do Webpack.
- Instale as dependências:
cnpm install jquery -Scnpm install bootstrap@3 -S - Configure o Bootstrap no
main.js.
// Arquivo: src/main.js (trecho relevante)
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap'; // Importa o JavaScript do Bootstrap
// ...
- Configure o jQuery através do
vue.config.jspara que esteja disponível globalmente.
// Arquivo: vue.config.js
const webpack = require('webpack');
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.$': 'jquery',
Popper: ['popper.js', 'default'], // Para Bootstrap 4+, mas pode ser útil
}),
],
},
};
Design da Tabela de Módulos da Página Inicial do Backend
Com base na análise do design da página inicial, identificamos a necessidade de uma API para gerenciar os banners rotativos.
- API de Carrossel (Banners)
- (Outras APIs, como cursos recomendados, professores, depoimentos de alunos, podem ser planejadas para o futuro).
Para isso, criaremos um novo aplicativo Django chamado homepage.
python manage.py startapp homepage
Passos para a Criação de Tabelas
1. Definição de um Modelo Base Comum (common_models.py)
Para padronizar campos como datas de criação/atualização, status de exclusão e visibilidade, criamos um modelo abstrato.
# Arquivo: utils/common_models.py
from django.db import models
class BaseModel(models.Model):
data_criacao = models.DateTimeField(auto_now_add=True, verbose_name='Data de Criação')
data_atualizacao = models.DateTimeField(auto_now=True, verbose_name='Última Atualização')
excluido = models.BooleanField(default=False, verbose_name='Registro Excluído')
ativo = models.BooleanField(default=True, verbose_name='Visível no Site')
prioridade = models.IntegerField(default=0, verbose_name='Ordem de Exibição')
class Meta:
abstract = True # Este modelo não será criado no banco de dados, apenas herdado
ordering = ['prioridade', '-data_criacao'] # Ordem padrão
2. Criação do Modelo Banner no Aplicativo homepage (models.py)
Este modelo representará cada item no carrossel da página inicial.
# Arquivo: homepage/models.py
from django.db import models
from utils.common_models import BaseModel # Importe do caminho correto do projeto
class Banner(BaseModel):
titulo = models.CharField(max_length=64, unique=True, verbose_name='Título do Banner')
imagem_url = models.ImageField(upload_to='banners/', verbose_name='URL da Imagem')
url_destino = models.CharField(max_length=255, verbose_name='Link de Destino')
descricao = models.TextField(blank=True, verbose_name='Descrição Detalhada')
class Meta:
db_table = 'app_banners' # Nome da tabela no banco de dados
verbose_name = 'Banner do Carrossel'
verbose_name_plural = 'Banners do Carrossel'
def __str__(self):
return self.titulo
3. Execução das Migrações
python manage.py makemigrations homepage
python manage.py migrate homepage
Observação sobre ModelForm vs. Form e ModelSerializer vs. Serializer:
A principal diferença reside na vinculação com modelos de banco de dados. Form e Serializer são mais genéricos, usados para renderizar campos HTML, validar dados e exibir erros. Já ModelForm e ModelSerializer são especificamente projetados para interagir com modelos Django, facilitando a criação, atualização e serialização de instâncias de modelo, pois inferem automaticamente os campos e validações do modelo associado.
Solução para RuntimeError: Model class ... doesn't declare an explicit app_label:
Esse erro geralmente ocorre quando um modelo está em um aplicativo que não foi corretamente registrado em INSTALLED_APPS ou quando o caminho de importação é ambíguo. Certifique-se de que seu aplicativo (ex: homepage) esteja em INSTALLED_APPS e que as importações dentro dos seus modelos e serializers usem caminhos absolutos baseados no diretório raiz do projeto ou no próprio nome do aplicativo.
API de Carrossel para a Página Inicial
1. Implementação da Viewset
Utilizaremos o Django REST Framework para criar uma ViewSet de leitura (somente listagem) para os banners.
# Arquivo: homepage/views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from homepage.models import Banner
from homepage.serializers import BannerSerializer
from utils.response_handler import APIResponse # Classe de resposta customizada
class BannerAPIView(GenericViewSet, ListModelMixin):
queryset = Banner.objects.filter(excluido=False, ativo=True).order_by('prioridade')
serializer_class = BannerSerializer
def list(self, request, *args, **kwargs):
# Chama o método list da ListModelMixin para obter os dados serializados
response_data = super().list(request, *args, **kwargs)
# Retorna uma resposta customizada com o formato padronizado
return APIResponse(data=response_data.data)
2. Definição do Serializer
O Serializer converterá as instâncias do modelo Banner em representações de dados (JSON) e vice-versa.
# Arquivo: homepage/serializers.py
from rest_framework import serializers
from homepage.models import Banner
class BannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
fields = ['id', 'imagem_url', 'url_destino', 'titulo'] # Campos a serem expostos
3. Configuração das Rotas
Registramos a ViewSet com um router simples do DRF para gerar automaticamente as rotas para a API.
# Arquivo: project_name/urls.py (ou um arquivo de urls específico para a API)
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from homepage import views
# Cria um router para registrar ViewSets
api_router = SimpleRouter()
# Registra a ViewSet de banners
# A URL será: /api/v1/banners/
api_router.register('banners', views.BannerAPIView, basename='banner')
urlpatterns = [
# ... outras urls do projeto ...
path('api/v1/', include(api_router.urls)), # Inclui as urls geradas pelo router
]
Inserção de Dados via Painel Administrativo
Para facilitar a gestão de conteúdo, utilizaremos o painel administrativo do Django, que pode ser aprimorado com temas como o SimpleUI.
- Instale o SimpleUI (opcional, mas recomendado para uma melhor experiência):
pip install django-simpleui - Adicione
simpleuiehomepageaos seusINSTALLED_APPSemsettings.py.
# Arquivo: project_name/settings.py
INSTALLED_APPS = [
'simpleui', # Adicionar antes de 'django.contrib.admin'
'django.contrib.admin',
# ... outros apps do Django ...
'homepage', # Seu aplicativo de página inicial
# ...
]
Dica sobre criação de superusuários: Se você definir um campo como unique=True (ex: mobile para números de telefone) em seu modelo de usuário, certifique-se de fornecer um valor diferente para cada superusuário criado ou deixar o campo nulo se permitido, caso contrário, tentativas subsequentes de criar superusuários sem preencher esse campo podem falhar devido à violação da unicidade.
- Acesse o painel administrativo em
http://127.0.0.1:8000/admin, faça login com um superusuário e insira os dados dos banners.
Detalhamento do Problema de Cross-Origin (CORS)
Ao fazer requisições AJAX de um frontend para um backend que estão em origens diferentes, você encontrará o problema de Cross-Origin Resource Sharing (CORS). Isso ocorre devido à Política de Mesma Origem (Same-Origin Policy) imposta pelos navegadores, uma medida de segurança fundamental para prevenir interações maliciosas entre diferentes sites.
- Origem é definida pela combinação de protocolo (http/https), domínio (ex: exemplo.com) e porta (ex: 80, 443, 3000). Se qualquer um desses três componentes for diferente, as origens são consideradas distintas.
- Mesmo que a requisição seja bem-sucedida no servidor, o navegador pode bloquear o acesso à resposta se a política de mesma origem não for satisfeita, resultando em um erro CORS.
Observação sobre o arquivo hosts: O arquivo hosts do sistema operacional (localizado em C:\Windows\System32\drivers\etc\hosts no Windows) permite mapear nomes de domínio para endereços IP localmente. Por exemplo, adicionar 127.0.0.1 meudominio.com fará com que seu navegador resolva meudominio.com para o seu próprio computador. Isso é útil para testes de desenvolvimento, mas não resolve diretamente problemas de CORS entre origens diferentes na prática.
Soluções para Problemas de CORS
Existem várias abordagens para contornar restrições de CORS:
- CORS (Cross-Origin Resource Sharing): A solução mais comum e recomendada. Envolve o backend enviando cabeçalhos HTTP específicos na resposta para informar ao navegador que a requisição cross-origin é permitida.
- Proxy Reverso (Nginx): Um servidor como Nginx pode atuar como um proxy, redirecionando requisições do frontand para o backend, fazendo com que o frontend e o proxy pareçam ter a mesma origem.
- Proxy Node.js: Similar ao Nginx, um servidor Node.js pode ser configurado para receber requisições do frontend e reencaminhá-las ao backend.
- JSONP (JSON with Padding): Uma técnica mais antiga e limitada, que explora a capacidade da tag
<script>de carregar scripts de qualquer domínio. Funciona apenas para requisições GET e não é considerada segura ou flexível o suficiente para a maioria dos casos modernos.
Fluxo Básico de Requisições CORS:
Os navegadores categorizam requisições CORS em dois tipos:
- Requisições Simples: São requisições HTTP que atendem a critérios específicos (métodos GET, HEAD, POST; cabeçalhos limitados a
Accept,Accept-Language,Content-Language,Last-Event-ID,Content-Type[apenasapplication/x-www-form-urlencoded,multipart/form-data,text/plain]). Nesses casos, o navegador envia a requisição diretamente com um cabeçalhoOrigin. - Requisições Não-Simples: Qualquer requisição que não se enquadre nos critérios de requisição simples (ex: métodos PUT, DELETE, ou cabeçalhos customizados, ou
Content-Type: application/json). Para estas, o navegador envia uma "requisição de pré-checagem" (preflight request) com o métodoOPTIONSantes da requisição real. Esta pré-checagem pergunta ao servidor se a requisição real é permitida. Somente se a pré-checagem for bem-sucedida, a requisição real é enviada.
Implementando a Solução CORS (Manual)
Você pode criar um middleware personalizado no Django para adicionar os cabeçalhos CORS necessários nas respostas.
# Arquivo: utils/custom_middleware.py
from django.utils.deprecation import MiddlewareMixin
class MiddlewareCORSPersonalizado(MiddlewareMixin):
def process_response(self, request, response):
# Permite todas as origens (ideal para desenvolvimento, para produção, use domínios específicos)
response["Access-Control-Allow-Origin"] = "*"
# Lida com requisições de pré-checagem (OPTIONS)
if request.method == "OPTIONS":
response["Access-Control-Allow-Headers"] = "Content-Type, Authorization, X-Requested-With, token, client-id"
response["Access-Control-Allow-Methods"] = "DELETE, GET, OPTIONS, PATCH, POST, PUT"
response["Access-Control-Max-Age"] = "86400" # Cache da pré-checagem por 24 horas
return response
Em seguida, adicione este middleware ao seu settings.py, preferencialmente no início da lista MIDDLEWARE.
# Arquivo: project_name/settings.py
MIDDLEWARE = [
'utils.custom_middleware.MiddlewareCORSPersonalizado', # Seu middleware personalizado
'django.middleware.security.SecurityMiddleware',
# ... outros middlewares
]
Implementando a Solução CORS (com django-cors-headers)
A forma mais robusta e recomendada de gerenciar CORS no Django é usando a biblioteca django-cors-headers.
- Instale a biblioteca:
pip install django-cors-headers - Adicione
corsheadersaos seusINSTALLED_APPSemsettings.py.
# Arquivo: project_name/settings.py
INSTALLED_APPS = [
# ...
'corsheaders', # Deve ser adicionado
# ...
]
- Adicione o middleware de CORS. Ele deve vir antes de qualquer middleware que possa gerar respostas (como
CommonMiddlewareouCsrfViewMiddleware) e depois de qualquer middleware que lida com codificação.
# Arquivo: project_name/settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware', # Adicionado aqui
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
- Configure os cabeçalhos e métodos permitidos em
settings.py.
# Arquivo: project_name/settings.py
# Permite requisições de todas as origens. Em produção, considere usar CORS_ALLOWED_ORIGINS
CORS_ORIGIN_ALLOW_ALL = True
# Lista de métodos HTTP permitidos para requisições cross-origin
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
)
# Lista de cabeçalhos HTTP que podem ser incluídos em requisições cross-origin
# Inclua quaisquer cabeçalhos customizados que seu frontend possa enviar (ex: 'token', 'client-id')
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'pragma',
'token', # Exemplo de cabeçalho customizado
'client-id', # Outro exemplo
)
# Se CORS_ORIGIN_ALLOW_ALL for False, use CORS_ALLOWED_ORIGINS para listar origens específicas
# CORS_ALLOWED_ORIGINS = [
# "http://localhost:8080",
# "http://meufrontend.com",
# ]
# Para permitir o envio de credenciais (cookies, cabeçalhos de autorização)
CORS_ALLOW_CREDENTIALS = True
Lembre-se de sempre listar todos os cabeçalhos customizados que seu frontend envia (como token ou name) na configuração CORS_ALLOW_HEADERS para garantir que as requisições não sejam bloqueadas.