Modos de Renderização SSR e CSR
Charlie descobriu que todas as páginas do MegaShop usam SSR, colocando enorme pressão nos servidores — com milhões de páginas de produtos sendo renderizadas a cada requisição. O conteúdo da página inicial raramente muda, mas é renderizado novamente a cada requisição. O painel administrativo não requer SEO, mas é renderizado no servidor. A renderização híbrida permite que cada página escolha a estratégia ideal: SSG para a página inicial, ISR para listas de produtos, SSR para páginas de detalhes e CSR para o centro do usuário.
1. O Que Você Vai Aprender
- Configuração do modo de renderização routeRules: ssr/csr/swr/prerender/hydrate
- ISR (Incremental Static Regeneration): Cache SWR e Revalidação Sob Demanda
- Renderização Edge ESR: Computação de Borda CDN do Nitro
- Guia Prático de Renderização Híbrida: Escolhendo Diferentes Estratégias para Diferentes Páginas
- MegaShop: Design de Cache ISR para Páginas de Produtos com Milhões de Itens
2. Uma História Real de um Arquiteto
(1) Ponto Crítico: SSR em todo o site sobrecarrega o servidor
O MegaShop processa 5.000 requisições por segundo, todas via SSR. O conteúdo da página inicial muda apenas uma vez por dia, mas é renderizado a cada requisição. A lista de produtos é atualizada a cada hora, resultando na renderização repetida de um milhão de páginas de detalhes de produtos. O uso da CPU do servidor chegou a 90%, então Bob está planejando adicionar mais 10 servidores.
(2) Solução com Renderização Híbrida
O routeRules do Nuxt 3 permite que cada rota tenha sua própria estratégia de renderização:
// nuxt.config.ts
routeRules: {
'/': { prerender: true }, // Página inicial: build uma vez
'/products': { swr: 3600 }, // Lista de produtos: cache 1 hora
'/products/**': { swr: 86400 }, // Detalhe do produto: cache 1 dia
'/admin/**': { ssr: false } // Admin: somente cliente
}
(3) Benefícios: 85% de redução na carga do servidor
Após implementar a renderização híbrida, as requisições SSR caíram de 5.000 por segundo para 750 por segundo, o número de servidores foi reduzido de 10 para 3, e os custos foram cortados em 70%.
3. Modo de Renderização routeRules
(1) Árvore de Decisão de Renderização Híbrida
flowchart TB
A[Page Request] --> B{Needs SEO?}
B -->|No| C[CSR - ssr: false]
B -->|Yes| D{Content changes?}
D -->|Never| E[SSG - prerender: true]
D -->|Rarely| F[ISR - swr: seconds]
D -->|Frequently| G[SSR - default mode]
D -->|Varies by region| H[ESR - edge rendering]
C --> I[Admin / Dashboard / User Center]
E --> J[About / Legal / Landing]
F --> K[Product List / Category Pages]
G --> L[Product Detail / Search Results]
H --> M[Global Homepage / Regional Content]
(2) Referência Rápida da Opção routeRules
| Opção | Valor | Efeito | Cenários Aplicáveis |
|---|---|---|---|
| ssr | boolean | Interruptor SSR | false = CSR |
| csr | boolean | Interruptor CSR | Opção legada |
| swr | number/string | Duração do cache ISR (segundos) | Páginas atualizadas periodicamente |
| prerender | boolean | Pré-renderizar durante o build | Página estática |
| hydrate | boolean | Somente hidratar, não renderizar | Otimização especial |
| redirect | string | Redirecionamento 301/302 | Migração de URL |
| cors | boolean | Cross-origin | Rota de API |
| headers | object | Cabeçalhos de resposta personalizados | cache/segurança |
(1) ▶Exemplo: Configuração completa do routeRules
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Página inicial: pré-renderizar em tempo de build
'/': { prerender: true },
// Páginas estáticas
'/about': { prerender: true },
'/privacy': { prerender: true },
// Lista de produtos: ISR com cache de 1 hora
'/products': { swr: 3600 },
// Detalhe do produto: ISR com cache de 24 horas
'/products/**': { swr: 86400 },
// Categorias: ISR com cache de 6 horas
'/categories/**': { swr: 21600 },
// Admin: renderização somente no cliente
'/admin/**': { ssr: false },
// Páginas do usuário: somente cliente
'/profile/**': { ssr: false },
// API: cabeçalhos CORS
'/api/**': { cors: true },
// Redirecionamentos
'/shop/': { redirect: '/products/', redirectCode: 301 },
// Cabeçalhos personalizados para assets estáticos
'/_nuxt/**': { headers: { 'cache-control': 'max-age=31536000' } }
}
})
Saída:
// Execução Bem-sucedida
4. ISR Incremental Static Regeneration
(1) Fluxo de Trabalho do ISR
sequenceDiagram
participant U as User
participant C as CDN/Cache
participant S as Nuxt Server
participant D as Database
U->>C: GET /products/123
C->>C: Cache HIT? (dentro do período swr)
C-->>U: HTML em cache (instantâneo)
Note over C,S: Após expirar o período swr
U->>C: GET /products/123
C->>C: Cache STALE
C-->>U: HTML obsoleto (ainda rápido)
C->>S: Revalidação em segundo plano
S->>D: Buscar dados mais recentes
D-->>S: Dados atualizados
S-->>C: HTML atualizado
Note over C: Próxima requisição obtém HTML atualizado
(2) Comparação entre ISR, SSR e SSG
| Dimensão | SSR | SSG (prerender) | ISR (swr) |
|---|---|---|---|
| Momento da Renderização | Em Cada Requisição | Durante o Build | Atualização em Segundo Plano Após Expiração do Cache |
| Tempo de Resposta | 🟡 Médio | 🟢 Mais Rápido | 🟢 Rápido (Cache Hit) |
| Atualidade dos Dados | ✅ Mais recentes | ❌ Fixo em tempo de build | ⚠️ Atraso máximo = swr segundos |
| Carga do Servidor | 🔴 Alta | 🟢 Mais Baixa | 🟢 Baixa |
| Casos de Uso | Dados em Tempo Real | Nunca Muda | Atualizações Programadas |
(1) ▶Exemplo: Página de Lista de Produtos ISR
// nuxt.config.ts - Configuração ISR
export default defineNuxtConfig({
routeRules: {
// Lista de produtos: servir do cache, revalidar a cada 60 segundos
'/products': { swr: 60 },
// Detalhe do produto: cache por 1 hora
'/products/**': { swr: 3600 }
}
})
Saída:
// Execução Bem-sucedida
(2) ▶Exemplo: Invalidação Programática do Cache ISR
// server/api/invalidate-cache.post.ts
export default defineEventHandler(async (event) => {
const { path } = await readBody(event)
// Limpar rota em cache específica
await useStorage('cache').removeItem(`nitro:routes:${path}`)
return { message: `Cache invalidated for ${path}`, timestamp: Date.now() }
})
Saída:
// Execução Bem-sucedida
5. Renderização Edge ESR
(1) Princípios da Renderização Edge
A ESR (Edge Side Rendering) realiza a renderização em nós de borda CDN, e os usuários obtêm o HTML do nó mais próximo.
| Dimensão | SSR Centralizado | ESR |
|---|---|---|
| Localização do Servidor | Região Única | Nós de Borda Globais |
| Latência | Depende da distância | Mínima (acesso local) |
| Casos de Uso | Usuários de Região Única | Usuários Globais |
| Presets do Nitro | node-server | cloudflare-pages / vercel-edge |
(1) ▶Exemplo: Configuração de Deploy na Vercel Edge
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'vercel-edge'
},
routeRules: {
// Página inicial renderizada na borda para usuários globais
'/': { swr: 3600 },
'/products/**': { swr: 86400 }
}
})
Saída:
// Execução Bem-sucedida
6. Estratégia de Renderização Híbrida do MegaShop
(1) Planejando a Estratégia de Renderização de Páginas do MegaShop
| Página | URL | Estratégia | SWR | Motivo |
|---|---|---|---|---|
| Página Inicial | / | SSG | - | Conteúdo estável, renderizado durante o build |
| Página Sobre | /about | SSG | - | Quase Nunca Muda |
| Lista de Produtos | /products | ISR | 3600 | Produtos atualizados a cada hora |
| Página de Categoria | /categories/** | ISR | 21600 | Atualizada Diariamente |
| Detalhes do Produto | /products/** | ISR | 86400 | Atualizado Diariamente |
| Resultados de Busca | /search | SSR | - | Consulta em Tempo Real |
| Carrinho de Compras | /cart | CSR | - | Somente para Membros |
| Centro do Usuário | /profile/** | CSR | - | SEO não necessário |
| Painel Admin | /admin/** | CSR | - | SEO não necessário |
| API | /api/** | - | - | API somente de dados |
(1) ▶Exemplo: routeRules completo para o MegaShop
// nuxt.config.ts
export default defineNuxtConfig({
ssr: true,
routeRules: {
// SSG: Pré-renderizar em tempo de build
'/': { prerender: true },
'/about': { prerender: true },
'/privacy': { prerender: true },
'/contact': { prerender: true },
// ISR: Cache com revalidação
'/products': { swr: 3600 }, // 1 hora
'/categories/**': { swr: 21600 }, // 6 horas
'/products/**': { swr: 86400 }, // 24 horas
// SSR: Renderização em tempo real
'/search': { swr: 0 },
// CSR: Somente no lado do cliente
'/cart': { ssr: false },
'/checkout/**': { ssr: false },
'/profile/**': { ssr: false },
'/admin/**': { ssr: false },
// API
'/api/**': { cors: true },
// Assets estáticos: cache longo
'/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } },
'/images/**': { headers: { 'cache-control': 'public, max-age=86400' } }
}
})
Saída:
// Execução Bem-sucedida
(2) Solução para Expiração do Cache ISR em Páginas de Produtos com Milhões de Itens
| Evento | Política de Expiração | Implementação |
|---|---|---|
| Atualização de Preço do Produto | Invalidar Página do Produto | useStorage('cache').removeItem() |
| Novos Produtos | Expirar Lista de Produtos | Expirar em Lote o Cache /products |
| Mudanças de Categoria | Invalidar Páginas de Categoria | Invalidar /categories/xxx |
| Promoção em Todo o Site | Limpar Todas as Páginas de Produtos | Limpar Cache Inteiro |
| Mudanças de Estoque | Expirar Produtos | Gatilho Webhook |
7. Exemplo Completo: Verificando os Modos de Renderização do MegaShop
<!-- pages/admin/dashboard.vue - Modo CSR -->
<template>
<div>
<h1>Admin Dashboard</h1>
<p>Rendering mode: Client-Side Only</p>
<p>Current time: {{ currentTime }}</p>
</div>
</template>
<script setup lang="ts">
// CSR: esta página NÃO é renderizada no servidor
definePageMeta({ ssr: false, layout: 'sidebar' })
const currentTime = ref(new Date().toISOString())
onMounted(() => {
setInterval(() => {
currentTime.value = new Date().toISOString()
}, 1000)
})
</script>
<!-- pages/products/[id].vue - Modo ISR -->
<template>
<div v-if="product">
<h1>{{ product.name }}</h1>
<p>${{ product.price }} USD</p>
<p>Cache: revalidated every 24 hours</p>
</div>
</template>
<script setup lang="ts">
// ISR: cache por 24 horas via routeRules swr: 86400
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>
❓Perguntas Frequentes
swr: 60 indica um cache de 60 segundos; após expirar, a próxima requisição dispara uma atualização em segundo plano, mas a requisição atual ainda retorna os dados em cache antigos.{ prerender: true, swr: 3600 } indica pré-renderização durante o processo de build, seguida de atualização usando a estratégia de cache ISR.<div id="__nuxt"></div> e o bundle JS); todo o conteúdo é renderizado no lado do cliente.x-nuxt-render-mode também indica o modo de renderização.📖Resumo
routeRulesconfigura uma estratégia de renderização separada para cada rota: SSR/CSR/SSG/ISR/ESR- ISR (swr) oferece o equilíbrio ideal: cache hits são tão rápidos quanto SSG, e a atualização automática ao expirar garante a atualidade dos dados
- ESR renderiza na borda CDN, fornecendo a menor latência para usuários globais
- Estratégia Híbrida do MegaShop: Página Inicial (SSG) + Produtos (ISR) + Busca (SSR) + Administração (CSR)
- Para páginas de produtos com milhões de itens, ISR usa cache baseado em visitas combinado com invalidação orientada a eventos; nem todas as páginas são pré-cacheadas
📝Exercícios
- Exercício Básico (Dificuldade: ⭐): Configure
routeRulespara habilitar pré-renderização da página inicial e CSR para a página admin, então verifique as diferenças na saída "Ver Código Fonte" entre os dois modos. - Exercício Avançado (Dificuldade ⭐⭐): Configure ISR (swr: 60) para a lista de produtos, e após 60 segundos, atualize a página para ver se os dados foram atualizados.
- Desafio (Dificuldade: ⭐⭐⭐): Implemente a API de invalidação de cache ISR. Quando os dados do produto forem atualizados, chame
/api/invalidate-cachepara invalidar o cache correspondente e verifique se a próxima requisição obtém os novos dados.
---|



