404 Not Found

404 Not Found


nginx

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


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:

TYPESCRIPT
// 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

100%
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

TYPESCRIPT
// 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:

TEXT
// Execução Bem-sucedida

4. ISR Incremental Static Regeneration

(1) Fluxo de Trabalho do ISR

100%
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

TYPESCRIPT
// 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:

TEXT
// Execução Bem-sucedida

(2) ▶Exemplo: Invalidação Programática do Cache ISR

TYPESCRIPT
// 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:

TEXT
// 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

TYPESCRIPT
// 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:

TEXT
// 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

TYPESCRIPT
// 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:

TEXT
// 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

VUE
<!-- 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>
VUE
<!-- 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

P Qual é a unidade de medida do swr?
R Segundos. 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.
P prerender e SWR podem ser usados juntos?
R Sim. { prerender: true, swr: 3600 } indica pré-renderização durante o processo de build, seguida de atualização usando a estratégia de cache ISR.
P Onde o cache ISR é armazenado?
R Depende do preset do Nitro. O preset Node.js armazena na memória ou no sistema de arquivos; o preset Vercel usa Vercel KV; e Cloudflare usa armazenamento KV.
P O que a página CSR retorna durante o SSR?
R Retorna um template HTML vazio (contendo apenas <div id="__nuxt"></div> e o bundle JS); todo o conteúdo é renderizado no lado do cliente.
P Como posso verificar qual modo de renderização a página atual está usando?
R Veja o código fonte (Ctrl+U) — se houver conteúdo, é SSR/SSG/ISR; se estiver vazio, é CSR. O cabeçalho de resposta x-nuxt-render-mode também indica o modo de renderização.
P O cache ISR para milhões de páginas de produtos vai encher o armazenamento?
R Produtos populares permanecem naturalmente no cache, enquanto os menos populares são automaticamente removidos após expirar. Não há necessidade de pré-cachear todos os milhões de páginas; apenas as que foram acessadas são cacheadas.

📖Resumo


📝Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Configure routeRules para 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.
  2. 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.
  3. Desafio (Dificuldade: ⭐⭐⭐): Implemente a API de invalidação de cache ISR. Quando os dados do produto forem atualizados, chame /api/invalidate-cache para invalidar o cache correspondente e verifique se a próxima requisição obtém os novos dados.

---|

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%