1.8 — Vite para Diferentes Frameworks
Entenda como o Vite se integra com React, Vue e Svelte, preparando-se para o restante do curso.
Objetivos da Aula
- Entender como plugins de framework funcionam
- Comparar a integração do Vite com React, Vue e Svelte
- Configurar um projeto Svelte com Vite (preparação para o próximo módulo)
- Conhecer o
vite-plugin-svelteem detalhes
Como Plugins de Framework Funcionam
Cada framework precisa de transformações específicas:
PLUGINS DE FRAMEWORK
React (.jsx/.tsx)
JSX
<App />
▶
Babel ou esbuild
▶
JavaScript
React.createElement()
Vue (.vue)
SFC Vue
<template>
▶
@vue/compiler-sfc
▶
JavaScript + CSS
Svelte (.svelte)
Componente Svelte
▶
Svelte Compiler
▶
JavaScript Vanilla
Vite + React
Setup
npm create vite@latest meu-app-react -- --template react
# ou com TypeScript
npm create vite@latest meu-app-react -- --template react-ts Configuração
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
react({
// Opções do Babel
babel: {
plugins: ['@emotion/babel-plugin']
},
// Fast Refresh
fastRefresh: true
})
]
}) Alternativa: React + SWC (mais rápido)
npm create vite@latest meu-app -- --template react-swc import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [react()]
}) Estrutura Típica
src/
├── App.jsx
├── main.jsx
├── index.css
└── components/
└── Button.jsx // main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
) Vite + Vue
Setup
npm create vite@latest meu-app-vue -- --template vue
# ou com TypeScript
npm create vite@latest meu-app-vue -- --template vue-ts Configuração
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
// Opções do compilador Vue
template: {
compilerOptions: {
// Tratar tags com hífen como custom elements
isCustomElement: (tag) => tag.includes('-')
}
}
})
]
}) Estrutura Típica
src/
├── App.vue
├── main.js
├── style.css
└── components/
└── Button.vue // main.js
import { createApp } from 'vue'
import App from './App.vue'
import './style.css'
createApp(App).mount('#app') <!-- App.vue -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">Count: {{ count }}</button>
</template>
<style scoped>
button { color: blue; }
</style> Vite + Svelte ⭐
Este é o foco do nosso curso!
Setup
npm create vite@latest meu-app-svelte -- --template svelte
# ou com TypeScript
npm create vite@latest meu-app-svelte -- --template svelte-ts Configuração Básica
// vite.config.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()]
}) Configuração Avançada
// vite.config.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [
svelte({
// Opções do compilador Svelte
compilerOptions: {
// Modo de desenvolvimento
dev: true,
// CSS em arquivo separado ou injetado
css: 'injected', // 'external' | 'injected' | 'none'
// Habilitar runes (Svelte 5)
runes: true,
// Gerar código acessível
accessors: false,
// Preservar espaços em branco
preserveWhitespace: false
},
// Pré-processadores
preprocess: [
// vitePreprocess() // Para TypeScript, SCSS, etc.
],
// Hot Module Replacement
hot: {
// Preservar estado local durante HMR
preserveLocalState: true
},
// Extensões de arquivo
extensions: ['.svelte'],
// Modo de emissão de CSS
emitCss: true
})
]
}) Estrutura de um Projeto Svelte
meu-app-svelte/
├── index.html
├── package.json
├── svelte.config.js # Configuração do Svelte
├── vite.config.js # Configuração do Vite
├── src/
│ ├── App.svelte # Componente raiz
│ ├── main.js # Ponto de entrada
│ ├── app.css # Estilos globais
│ ├── lib/ # Componentes reutilizáveis
│ │ └── Counter.svelte
│ └── vite-env.d.ts # Tipos do Vite (TS)
└── public/
└── vite.svg Arquivos Principais
<!-- index.html -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Meu App Svelte</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html> // src/main.js
import './app.css'
import App from './App.svelte'
const app = new App({
target: document.getElementById('app')
})
export default app <!-- src/App.svelte -->
<script>
import Counter from './lib/Counter.svelte'
let name = 'mundo'
</script>
<main>
<h1>Olá, {name}!</h1>
<Counter />
</main>
<style>
main {
text-align: center;
padding: 2rem;
}
h1 {
color: #ff3e00;
}
</style> <!-- src/lib/Counter.svelte -->
<script>
let count = 0
function increment() {
count += 1
}
</script>
<button on:click={increment}>
Cliques: {count}
</button>
<style>
button {
font-size: 1.5rem;
padding: 0.5rem 1rem;
cursor: pointer;
}
</style> Comparativo: O Mesmo Componente
React
// Counter.jsx
import { useState } from 'react'
import './Counter.css'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(c => c + 1)}>
Cliques: {count}
</button>
)
} Vue
<!-- Counter.vue -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">
Cliques: {{ count }}
</button>
</template>
<style scoped>
button { /* estilos */ }
</style> Svelte
<!-- Counter.svelte -->
<script>
let count = 0
</script>
<button on:click={() => count++}>
Cliques: {count}
</button>
<style>
button { /* estilos - escopo automático! */ }
</style> Comparação
| Aspecto | React | Vue | Svelte |
|---|---|---|---|
| Estado | useState hook | ref() | let simples |
| Eventos | onClick | @click | on:click |
| Interpolação | {valor} | {{ valor }} | {valor} |
| CSS | Externo/CSS-in-JS | <style scoped> | <style> (escopo auto) |
| Bundle | Runtime React | Runtime Vue | Zero runtime |
Pré-processadores com Svelte
Instalação do vitePreprocess
npm install -D @sveltejs/vite-plugin-svelte
npm install -D sass # Para SCSS
npm install -D typescript # Para TS // vite.config.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [
svelte({
preprocess: vitePreprocess()
})
]
}) Usando TypeScript
<!-- Componente.svelte -->
<script lang="ts">
interface User {
name: string
age: number
}
export let user: User
let count: number = 0
</script>
<p>{user.name} tem {user.age} anos</p> Usando SCSS
<!-- Componente.svelte -->
<style lang="scss">
$primary: #ff3e00;
button {
background: $primary;
&:hover {
background: darken($primary, 10%);
}
}
</style> svelte.config.js
Além do vite.config.js, projetos Svelte têm um arquivo de configuração específico:
// svelte.config.js
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
// Pré-processadores (TS, SCSS, etc)
preprocess: vitePreprocess(),
// Opções do compilador
compilerOptions: {
// Svelte 5 runes
runes: true
},
// Avisos a ignorar
onwarn: (warning, handler) => {
// Ignora avisos de acessibilidade específicos
if (warning.code === 'a11y-click-events-have-key-events') return
handler(warning)
},
// Extensões de arquivo
extensions: ['.svelte']
} 🎯 Mini-Projeto: Migrando para Svelte
Vamos criar a versão Svelte do nosso Dashboard!
Passo 1: Criar Projeto Svelte
# Na pasta do curso
npm create vite@latest dashboard-svelte -- --template svelte
cd dashboard-svelte
npm install Passo 2: Configurar Aliases
// vite.config.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import path from 'path'
export default defineConfig({
plugins: [svelte()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils')
}
},
server: {
port: 3000,
open: true
}
}) Passo 3: Componente PerformanceCard
<!-- src/components/PerformanceCard.svelte -->
<script>
export let titulo = ''
export let valor = 0
export let unidade = 'ms'
</script>
<div class="performance-card">
<h3 class="card-title">{titulo}</h3>
<p class="card-value">
{valor}<span class="card-unit">{unidade}</span>
</p>
</div>
<style>
.performance-card {
background: #242424;
border-radius: 12px;
padding: 1.5rem;
transition: transform 0.2s, box-shadow 0.2s;
}
.performance-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}
.card-title {
font-size: 0.875rem;
color: #a0a0a0;
margin-bottom: 0.5rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.card-value {
font-size: 2rem;
font-weight: 700;
color: #4ade80;
margin: 0;
}
.card-unit {
font-size: 0.875rem;
color: #a0a0a0;
margin-left: 0.25rem;
}
</style> Passo 4: Componente App Principal
<!-- src/App.svelte -->
<script>
import { onMount } from 'svelte'
import PerformanceCard from '@components/PerformanceCard.svelte'
let metricas = {
domReady: 0,
pageLoad: 0,
fcp: 'N/A',
renderTime: 0
}
const inicioRender = performance.now()
onMount(() => {
// Calcula tempo de render
metricas.renderTime = (performance.now() - inicioRender).toFixed(2)
// Espera página carregar para métricas completas
window.addEventListener('load', () => {
const timing = performance.timing
metricas.domReady = timing.domContentLoadedEventEnd - timing.navigationStart
metricas.pageLoad = timing.loadEventEnd - timing.navigationStart
// FCP
const paintEntries = performance.getEntriesByType('paint')
const fcp = paintEntries.find(e => e.name === 'first-contentful-paint')
metricas.fcp = fcp ? fcp.startTime.toFixed(2) : 'N/A'
})
})
</script>
<div class="dashboard">
<header class="header">
<img src="/vite.svg" class="logo" alt="Vite logo" />
<h1>Dashboard Svelte</h1>
<p class="subtitle">Agora com o poder do Svelte! ⚡</p>
</header>
<main class="main">
<section class="section">
<h2>📊 Métricas de Performance</h2>
<div class="cards-grid">
<PerformanceCard titulo="DOM Ready" valor={metricas.domReady} />
<PerformanceCard titulo="Page Load" valor={metricas.pageLoad} />
<PerformanceCard titulo="FCP" valor={metricas.fcp} />
<PerformanceCard titulo="Render" valor={metricas.renderTime} />
</div>
</section>
</main>
<footer class="footer">
<p>Feito com Svelte + Vite</p>
</footer>
</div>
<style>
:global(body) {
margin: 0;
font-family: Inter, system-ui, -apple-system, sans-serif;
background-color: #0f0f0f;
color: #ffffff;
}
.dashboard {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.header {
text-align: center;
margin-bottom: 3rem;
}
.logo {
width: 80px;
height: 80px;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.header h1 {
font-size: 2.5rem;
margin: 1rem 0 0.5rem;
background: linear-gradient(135deg, #ff3e00, #ff8a00);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
color: #a0a0a0;
margin: 0;
}
.section {
margin-bottom: 2rem;
}
.section h2 {
margin-bottom: 1rem;
font-size: 1.25rem;
color: #a0a0a0;
}
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.footer {
margin-top: 3rem;
text-align: center;
color: #666;
}
</style> Passo 5: Testar
npm run dev
# Acesse http://localhost:3000
npm run build
# Observe: bundle MUITO menor que React/Vue! ✅ Desafio da Aula
Objetivo
Adicionar um componente Counter interativo ao Dashboard Svelte.
Instruções
- Crie
src/components/Counter.svelte - O contador deve ter botões de + e -
- Adicione o componente ao App.svelte
- Bônus: Adicione uma animação quando o valor muda
Spec de Verificação
- O contador aparece no dashboard
- Clicar em + aumenta o valor
- Clicar em - diminui o valor
- Bônus: Há animação visual na mudança
Solução
🔍 Clique para ver a solução
<!-- src/components/Counter.svelte -->
<script>
let count = 0
function increment() {
count += 1
}
function decrement() {
count -= 1
}
</script>
<div class="counter-card">
<h3>Contador Interativo</h3>
<div class="counter-display">
<button on:click={decrement} class="btn-minus">−</button>
<span class="count" class:bump={count !== 0}>{count}</span>
<button on:click={increment} class="btn-plus">+</button>
</div>
</div>
<style>
.counter-card {
background: #242424;
border-radius: 12px;
padding: 1.5rem;
text-align: center;
}
h3 {
margin: 0 0 1rem;
color: #a0a0a0;
font-size: 0.875rem;
text-transform: uppercase;
}
.counter-display {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
button {
width: 48px;
height: 48px;
border: none;
border-radius: 50%;
font-size: 1.5rem;
cursor: pointer;
transition: transform 0.1s, background 0.2s;
}
button:hover {
transform: scale(1.1);
}
button:active {
transform: scale(0.95);
}
.btn-plus {
background: #4ade80;
color: #000;
}
.btn-minus {
background: #ef4444;
color: #fff;
}
.count {
font-size: 3rem;
font-weight: 700;
min-width: 80px;
color: #fff;
transition: transform 0.1s;
}
.count.bump {
animation: bump 0.1s ease-out;
}
@keyframes bump {
50% { transform: scale(1.2); }
}
</style> No App.svelte, adicione:
<script>
import Counter from '@components/Counter.svelte'
</script>
<!-- Na section -->
<Counter />🎉 Conclusão do Módulo 1
Parabéns! Você completou o Módulo 1 sobre Fundamentos do Vite.
O que você aprendeu
- ✅ Por que o Vite é mais rápido que bundlers tradicionais
- ✅ Arquitetura de ESModules nativos e HMR
- ✅ Criação e estrutura de projetos Vite
- ✅ Configuração avançada com
vite.config.js - ✅ Sistema de plugins e criação de plugins customizados
- ✅ Variáveis de ambiente e múltiplos modos
- ✅ Build de produção e otimização
- ✅ Integração com diferentes frameworks (React, Vue, Svelte)
Próximos passos
No Módulo 2, vamos fazer uma comparação profunda entre Svelte e React, entendendo as diferenças filosóficas e práticas entre os dois frameworks.
Próximo módulo: Módulo 2 — Svelte vs React: Entendendo as Diferenças