Voltar ao Blog
    Performance
    Frontend

    Vite: Otimizando Builds Para Produção na Prática

    Code splitting manual, análise de bundle e compressão Brotli. Como reduzi 40% do bundle size deste site.

    Publicado em ·9 min de leitura·por Bernardo Gomes

    O problema com o bundle padrão

    Vite gera um bundle otimizado por padrão, mas sem configuração manual, bibliotecas grandes como framer-motion, lucide-react e react-query acabam no mesmo chunk que o código da aplicação — o que significa que uma mudança pequena invalida o cache de tudo.

    Manual chunks: separando vendor do app

    A chave para cache-busting eficiente é separar dependências (que mudam raramente) do código da aplicação (que muda sempre). O Rollup — engine do Vite em produção — expõe manualChunks exatamente para isso.

    typescript
    // vite.config.ts
    export default defineConfig({
      build: {
        rollupOptions: {
          output: {
            manualChunks: {
              vendor: ["react", "react-dom"],
              router: ["react-router-dom"],
              query: ["@tanstack/react-query"],
              motion: ["framer-motion"],
              icons: ["lucide-react"],
              ui: [
                "@radix-ui/react-dialog",
                "@radix-ui/react-dropdown-menu",
                // ... outros radix
              ],
            },
          },
        },
      },
    });
    💡

    Separe ícones em chunk próprio. lucide-react tem centenas de exports e mesmo com tree-shaking pode ser grande dependendo de quantos você usa.

    Analisando o bundle com rollup-plugin-visualizer

    Antes de otimizar, meça. O rollup-plugin-visualizer gera um treemap interativo que mostra exatamente quanto espaço cada módulo ocupa.

    typescript
    import { visualizer } from "rollup-plugin-visualizer";
    
    export default defineConfig({
      plugins: [
        visualizer({
          open: true,         // abre no browser automaticamente
          filename: "dist/stats.html",
          gzipSize: true,
          brotliSize: true,
        }),
      ],
    });

    Compressão Brotli + Gzip

    O Vite não gera arquivos comprimidos por padrão — deixa para o servidor fazer on-the-fly. Pré-comprimir em build time é mais eficiente: o servidor serve o arquivo já comprimido sem CPU extra por request.

    typescript
    import viteCompression from "vite-plugin-compression";
    
    export default defineConfig({
      plugins: [
        viteCompression({ algorithm: "brotliCompress", ext: ".br" }),
        viteCompression({ algorithm: "gzip", ext: ".gz" }),
      ],
    });

    Resultado

    • Bundle total: 847KB → 512KB (39% menor após Brotli)
    • Cache hit rate: subiu de ~30% para ~85% com chunk separation
    • First load JS: 243KB → 148KB (chunks lazy-loaded separados)
    • LCP: 2.8s → 1.1s em mobile (com lazy loading de rotas + imagens)
    ℹ️

    Números do Lighthouse simulando conexão 4G lenta. Resultados variam, mas o padrão de melhora com code splitting + compressão é consistente.