Otimização de Desempenho
O MegaShop está no ar, mas sua pontuação no Lighthouse é apenas 45 — leva 4,5 segundos para carregar a primeira tela, as imagens não estão otimizadas, e o bundle JS é muito grande. Charlie precisa de uma pontuação Lighthouse de 90+ e um LCP de menos de 2 segundos na primeira tela para se manter competitivo.
1. O Que Você Vai Aprender
- Code Splitting e Tree Shaking: Otimização de Build Vite e Análise de Bundle
- Otimização de imagens: @nuxt/image + WebP + lazy loading + srcset responsivo
- Lazy Loading de Componentes e Processamento Assíncrono: Prefixo "Lazy" + Suspense
- Estratégia de Caching: Cabeçalhos de Cache HTTP + Nitro KV + CDN
- Prática de Otimização Lighthouse 90+ do MegaShop
2. Uma História Real de um Arquiteto
(1) Ponto Crítico: Pontuação Lighthouse de 45, 4,5 segundos para a primeira pintura de conteúdo
Alice abriu o MegaShop no celular e levou 4,5 segundos para os produtos aparecerem. Charlie executou uma auditoria Lighthouse e descobriu que o bundle JS tinha 2,8 MB, todas as imagens eram PNGs não comprimidos, não havia cabeçalhos de cache, e o código de 50 componentes carregava na primeira tela.
(2) Solução com Otimização de Desempenho do Nuxt 3
O Nuxt 3 inclui uma variedade de recursos de otimização de desempenho embutidos:
// Otimização de imagens com @nuxt/image
<NuxtImg src="/products/123.webp" width="400" height="400" loading="lazy" />
(3) Desempenho: Pontuação Lighthouse de 92, LCP de 1,3 segundos
Após a otimização, o tamanho do bundle JS foi reduzido para 180 KB (gzip), imagens são automaticamente servidas como WebP e são responsivas, o LCP da primeira tela é de 1,3 segundos, e a pontuação Lighthouse é 92.
3. Code Splitting e Tree Shaking
(1) Árvore de Decisão de Otimização de Desempenho
flowchart TB
A[Performance Issue] --> B{What type?}
B -->|Slow first paint| C[Code Splitting]
B -->|Large images| D[Image Optimization]
B -->|Slow subsequent loads| E[Caching Strategy]
B -->|JS too large| F[Tree Shaking]
C --> C1[Lazy components]
C --> C2[Dynamic imports]
D --> D1[@nuxt/image module]
D --> D2[WebP conversion]
E --> E1[HTTP Cache Headers]
E --> E2[Nitro KV Cache]
F --> F1[Check bundle with rollup-plugin-visualizer]
(2) Análise de Bundle
npm install -D rollup-plugin-visualizer
// nuxt.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
export default defineNuxtConfig({
vite: {
plugins: [
visualizer({ filename: 'bundle-stats.html', open: true })
],
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'vue-router'],
'pinia': ['pinia']
}
}
}
}
}
})
(1) ▶Exemplo: Code Splitting com Lazy Component
<template>
<div>
<!-- Essencial: carregado imediatamente -->
<AppHeader />
<ProductHero :product="featured" />
<!-- Abaixo da dobra: lazy loading -->
<LazyProductGrid :products="all" />
<LazyProductReviews :product-id="id" />
<!-- Condicional: carregado apenas quando visível -->
<LazyNewsletterSignup v-if="showNewsletter" />
</div>
</template>
Saída:
// Execução Bem-sucedida
(3) Comparação Antes e Depois da Otimização
| Métrica | Antes da Otimização | Após a Otimização | Melhoria |
|---|---|---|---|
| Bundle JS (gzip) | 2,8 MB | 180 KB | -94% |
| Número de componentes na primeira tela | 50 | 8 | -84% |
| Download JS na Primeira Tela | 2,8 MB | 85 KB | -97% |
| LCP | 4,5s | 1,3s | -71% |
4. Otimização de Imagens
(1) ▶Exemplo: Instalando @nuxt/image
npm install @nuxt/image
Saída:
# Comando executado com sucesso
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
quality: 80,
format: ['webp', 'avif'],
screens: {
xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280, xxl: 1536
}
}
})
(2) ▶Exemplo: NuxtImg Imagens Responsivas
<template>
<div>
<!-- Auto WebP/AVIF + srcset responsivo -->
<NuxtImg
src="/images/product-123.jpg"
width="800"
height="600"
format="webp"
loading="lazy"
sizes="sm:100vw md:50vw lg:33vw"
:modifiers="{ quality: 80 }"
alt="Premium Headphones"
/>
<!-- Placeholder com blur-up -->
<NuxtImg
src="/images/hero.jpg"
placeholder
width="1920"
height="1080"
format="webp"
alt="MegaShop Hero"
/>
</div>
</template>
Saída:
// Execução Bem-sucedida
(3) ▶Exemplo: NuxtPicture Multi-Formato
<template>
<!-- NuxtPicture: auto-seleciona melhor formato por navegador -->
<NuxtPicture
src="/images/banner.jpg"
width="1200"
height="400"
format="avif,webp"
loading="lazy"
alt="Sale Banner"
/>
</template>
Saída:
// Execução Bem-sucedida
(1) Comparação de Formatos de Imagem
| Formato | Taxa de Compressão | Suporte de Navegadores | Casos de Uso |
|---|---|---|---|
| AVIF | 🟢 Melhor (50% vs. JPG) | Chrome/Edge/Firefox | Navegadores modernos |
| WebP | 🟢 Alta (30% vs. JPG) | Todos os principais navegadores | Formato preferido de uso geral |
| JPEG | 🟡 Referência | Todos | Fallback de compatibilidade |
| PNG | 🔴 Grande | Todos | Imagens Transparentes |
5. Estratégia de Caching
(1) ▶Exemplo: Cabeçalhos de Cache HTTP
// nuxt.config.ts - Cabeçalhos de cache por rota
export default defineNuxtConfig({
routeRules: {
// Assets estáticos: cache imutável de 1 ano
'/_nuxt/**': {
headers: { 'cache-control': 'public, max-age=31536000, immutable' }
},
// Imagens: cache de 1 dia
'/images/**': {
headers: { 'cache-control': 'public, max-age=86400, stale-while-revalidate=604800' }
},
// Páginas de produtos: cache ISR
'/products/**': {
swr: 86400,
headers: { 'cache-control': 'public, s-maxage=86400, stale-while-revalidate=3600' }
}
}
})
Saída:
// Execução Bem-sucedida
(2) ▶Exemplo: API de Cache Nitro KV
// server/api/products/[id].get.ts - Handler com cache
export default cachedEventHandler(
async (event) => {
const id = getRouterParam(event, 'id')
const product = await fetchProductFromDB(Number(id))
return product
},
{
maxAge: 60 * 60, // Cache por 1 hora
swr: true, // Servir obsoleto enquanto revalida
getKey: (event) => `product:${getRouterParam(event, 'id')}`,
varies: ['Accept-Language'] // Cache diferente por idioma
}
)
Saída:
// Execução Bem-sucedida
(1) Comparação de Hierarquias de Cache
| Nível | Localização | Velocidade | Método de Controle |
|---|---|---|---|
| Cache do Navegador | Dispositivo do Usuário | ⚡⚡⚡ Mais rápido | Cabeçalho Cache-Control |
| Cache de Borda CDN | Nó Mais Próximo | ⚡⚡ Rápido | Configuração CDN + Vary |
| Cache Nitro KV | Memória do Servidor | ⚡ Rápido | cachedEventHandler |
| Cache de Página ISR | Nitro Storage | ⚡ Rápido | routeRules swr |
6. Exemplo Completo: Configuração de Otimização de Desempenho do MegaShop
// nuxt.config.ts - Otimização de desempenho completa
export default defineNuxtConfig({
ssr: true,
modules: ['@nuxt/image', '@nuxtjs/tailwindcss'],
// Otimização de imagens
image: {
quality: 80,
format: ['webp', 'avif'],
screens: { xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280 }
},
// Otimização de build Vite
vite: {
build: {
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('pinia')) return 'vendor-pinia'
if (id.includes('vue')) return 'vendor-vue'
return 'vendor-libs'
}
}
}
}
}
},
// Estratégia de cache em nível de rota
routeRules: {
'/': { prerender: true },
'/products': { swr: 3600 },
'/products/**': { swr: 86400 },
'/admin/**': { ssr: false },
'/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } },
'/images/**': { headers: { 'cache-control': 'public, max-age=86400' } }
},
// Dicas de desempenho do app
experimental: {
payloadExtraction: true // Reduzir tamanho do payload
}
})
<!-- pages/index.vue - Página inicial otimizada -->
<template>
<div>
<AppHeader />
<!-- Acima da dobra: carregamento imediato -->
<section class="hero">
<NuxtImg src="/images/hero.jpg" width="1200" height="400" format="webp"
priority alt="MegaShop" />
<h1>MegaShop</h1>
</section>
<!-- Abaixo da dobra: lazy loading -->
<LazyProductGrid :products="featured" />
<!-- Condicional: carregar apenas quando visível -->
<LazyNewsletterSignup v-if="showNewsletter" />
</div>
</template>
<script setup lang="ts">
const { data: featured } = await useFetch('/api/products/featured')
const showNewsletter = ref(false)
</script>
❓Perguntas Frequentes
image.domains para adicionar domínios externos: image: { domains: ['cdn.example.com'] }. NuxtImg otimizará automaticamente imagens externas.img comum?srcset, converte imagens para WebP/AVIF, corta para se ajustar às dimensões do dispositivo, e habilita lazy loading. Com uma tag img comum, você precisa lidar com todas essas otimizações manualmente.manualChunks?manualChunks deve ser usado apenas para dividir bibliotecas de terceiros.📖Resumo
- Code splitting: Lazy components + dynamic import + manualChunks; apenas o código necessário é carregado na primeira tela
- Otimização de imagens: @nuxt/image — WebP/AVIF automático, srcset responsivo e lazy loading
- Estratégia de caching: Navegador → CDN → ISR Nitro → renderização dinâmica, cache em múltiplas camadas
- Use
rollup-plugin-visualizerpara análise de bundle e identificar gargalos de tamanho - MegaShop após otimização: JS 180 KB (gzip) + LCP 1,3 s + pontuação Lighthouse 92
📝Exercícios
- Exercício Básico (Dificuldade: ⭐): Instale @nuxt/image, substitua todas as tags
imgporNuxtImg, e verifique a conversão automática para WebP. - Exercício Avançado (Dificuldade: ⭐⭐): Use
rollup-plugin-visualizerpara analisar o bundle, identifique a maior dependência, e configuremanualChunkspara dividi-la. - Desafio (Dificuldade: ⭐⭐⭐): Alcançe uma pontuação Lighthouse de 90+: Otimização de imagens + code splitting + estratégias de caching, usando Lighthouse CI para auditorias automatizadas
---|



