Build e Otimização
Aula 3 de 4
Build (Rollup)
Vite usa Rollup para produção, com configuração exposta via build.rollupOptions.
Configuração Básica
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: true,
minify: 'esbuild', // 'esbuild' (rápido) | 'terser' (menor) | false
target: 'es2020', // Alvo de transpilação
cssCodeSplit: true, // Separar CSS por chunk
cssMinify: 'lightningcss', // 'esbuild' | 'lightningcss'
reportCompressedSize: true,
chunkSizeWarningLimit: 500 // KB
}
});
Code Splitting
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
// Separar vendor chunks
manualChunks(id) {
if (id.includes('node_modules')) {
// Agrupar por biblioteca
if (id.includes('react')) return 'vendor-react';
if (id.includes('lodash')) return 'vendor-lodash';
if (id.includes('d3')) return 'vendor-d3';
return 'vendor'; // vendor genérico
}
},
// Nomes customizados
entryFileNames: 'assets/[name]-[hash].js',
chunkFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash][extname]'
}
}
}
});
manualChunks Avançado
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// Agrupar bibliotecas específicas
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown'],
'chart-vendor': ['recharts', 'd3']
}
}
}
}
});
Treeshaking
// vite.config.ts
export default defineConfig({
build: {
// Treeshaking é automático no Rollup
// Configurar opções adicionais
rollupOptions: {
treeshake: {
moduleSideEffects: [
'*.css',
'*.scss',
'regenerator-runtime/runtime'
],
propertyReadSideEffects: false,
tryCatchDeoptimization: false,
unknownGlobalSideEffects: false
}
}
}
});
ChunkSizeWarningLimit
export default defineConfig({
build: {
chunkSizeWarningLimit: 300, // alerta se chunk > 300KB
rollupOptions: {
output: {
manualChunks(id) {
// Garantir que chunks grandes sejam divididos
if (id.includes('node_modules')) {
if (id.includes('react')) return 'react';
if (id.includes('lodash')) {
// Dividir lodash em chunks menores
if (id.includes('lodash-es')) {
const parts = id.split('/');
const module = parts[parts.length - 2] || 'lodash';
return `lodash.${module}`;
}
}
return 'vendor';
}
}
}
}
}
});
Variáveis de Ambiente
// .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
// .env.development
VITE_API_URL=http://localhost:8080
// .env.production
VITE_API_URL=https://api.example.com
// Acesso no código
const apiUrl = import.meta.env.VITE_API_URL;
const mode = import.meta.env.MODE; // 'development' | 'production'
const isDev = import.meta.env.DEV;
const isProd = import.meta.env.PROD;
console.log(import.meta.env); // Tipo: ImportMetaEnv
Env por Modo
.env # Carregado em todos os ambientes
.env.local # Sobrescreve .env (não commitado)
.env.development # Modo development
.env.production # Modo production
.env.staging # Custom
# Usar modo customizado
vite build --mode staging
Multi-Page App
// vite.config.ts
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin/index.html'),
login: resolve(__dirname, 'login/index.html')
}
}
}
});
<!-- admin/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Admin</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/admin/src/main.tsx"></script>
</body>
</html>
Lib Mode
Para publicar bibliotecas (não apps).
// vite.config.ts
import { defineConfig } from 'vite';
import { resolve } from 'path';
import dts from 'vite-plugin-dts';
export default defineConfig({
plugins: [
dts({ // Gera .d.ts
insertTypesEntry: true
})
],
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'MyLibrary',
formats: ['es', 'cjs', 'umd'],
fileName: (format) => `my-lib.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'], // Peer dependencies
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
}
}
});
# Build da lib
npm run build
# Output
dist/
├── my-lib.es.js
├── my-lib.cjs.js
├── my-lib.umd.js
└── index.d.ts
Package.json para Lib
{
"name": "my-library",
"type": "module",
"files": ["dist"],
"main": "./dist/my-lib.cjs.js",
"module": "./dist/my-lib.es.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/my-lib.es.js",
"require": "./dist/my-lib.cjs.js",
"types": "./dist/index.d.ts"
}
}
}
Visualizer
npm install -D rollup-plugin-visualizer
// vite.config.ts
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({
filename: 'stats.html',
open: true,
gzipSize: true,
brotliSize: true
})
]
});
npm run build
# Abre stats.html com treemap interativo do bundle
Lab: Exercício - Build Otimizado
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
react(),
visualizer({ open: true })
],
build: {
sourcemap: false,
minify: 'terser',
chunkSizeWarningLimit: 300,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom', 'react-router-dom'],
ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu']
}
}
}
}
});
npm run build
# Analisar resultado
ls -lh dist/assets/
# Ver stats.html no navegador
Code splitting com manualChunks separa vendor do código da app. Variáveis de ambiente com prefixo VITE_ são incluídas no bundle. Lib mode gera bibliotecas em múltiplos formatos. Visualizer ajuda a identificar chunks grandes.