Pular para o conteúdo principal

[Lv2] Implementação SSR: Data Fetching e gestao de SEO Meta

Em um projeto Nuxt 3: implementar carregamento de dados em SSR e gestao dinâmica de SEO Meta para que buscadores indexem rotas dinâmicas corretamente.


1. Eixo da resposta na entrevista​

  1. Estratégia de data fetching: usar useFetch/useAsyncData para preload no servidor e entregar HTML completo para SEO.
  2. Meta tags dinâmicas: usar useHead ou useSeoMeta para gerar metadata por recurso.
  3. Performance: aplicar request deduplication, cache no servidor e separar claramente páginas SSR e CSR.

2. Uso correto de useFetch / useAsyncData​

2.1 Por que data fetching em SSR é importante​

Cenário típico:

  • Rotas dinâmicas (ex.: /products/[id]) precisam buscar dados de API.
  • Se carregar apenas no cliente, crawler pode ver conteúdo incompleto.
  • Objetivo: entregar HTML renderizado no servidor com dados completos.

Solução: usar useFetch ou useAsyncData no Nuxt 3.

2.2 Exemplo base com useFetch​

Arquivo: pages/products/[id].vue

// uso básico
const { data: product } = await useFetch(`/api/products/${route.params.id}`);

Opções importantes:

OpçãoObjetivoDefault
keyChave única para deduplicar requestsauto
lazyCarga adiada (não bloqueia SSR)false
serverExecutar no servidortrue
defaultValor fallbacknull
transformTransformar resposta antes do consumo-

2.3 Exemplo completo​

// pages/products/[id].vue
const { data: product } = await useFetch(`/api/products/${route.params.id}`, {
key: `product-${route.params.id}`, // evita requests duplicados
lazy: false, // SSR espera os dados
server: true, // garante execucao no servidor
default: () => ({
id: null,
name: '',
description: '',
image: '',
}),
transform: (data: any) => {
// normalização de dados
return {
...data,
formattedPrice: formatPrice(data.price),
};
},
});

Por que essas opções importam:

  1. key
    • Permite request deduplication.
    • Mesmo key -> um request efetivo.
  2. lazy: false
    • Servidor renderiza depois de receber dados.
    • Crawler recebe conteúdo final.
  3. server: true
    • Fetch roda no caminho SSR.
    • Evita depender somente do cliente.

2.4 useAsyncData vs useFetch​

CriteriouseFetchuseAsyncData
Uso principalChamada APIQualquer operação assíncrona
ConvenienciaURL/header integradosLogica manual
Caso comumHTTP data fetchingQuery de DB, agregacao, arquivos
// useFetch: focado em API
const { data } = await useFetch('/api/products/123');

// useAsyncData: lógica async livre
const { data } = await useAsyncData('products', async () => {
const result = await someAsyncOperation();
return result;
});

2.5 $fetch vs useFetch​

Regra curta para entrevista:

  • $fetch para ação do usuário (click, submit, refresh).
  • useFetch para carga inicial com sincronização SSR/Hydration.

$fetch características:

  • Cliente HTTP puro (ofetch)
  • Não transfere estado SSR
  • Uso direto em setup() pode gerar double fetch

useFetch características:

  • Combina useAsyncData + $fetch
  • Amigavel para hydration
  • Entrega data, pending, error, refresh

Comparação:

PontouseFetch$fetch
Transferencia SSRSimNão
RetornoRefs reativasPromise com dados brutos
Uso principalCarga inicial da páginaOperacoes dirigidas por evento
// correto: carga inicial
const { data } = await useFetch('/api/user');

// correto: ação de usuário
const submitForm = async () => {
await $fetch('/api/submit', { method: 'POST', body: form });
};

// evitar: setup + $fetch direto
const data = await $fetch('/api/user');

3. Gestao de SEO Meta com useHead​

3.1 Por que meta tags dinâmicas são necessárias​

Cenário típico:

  • Páginas de produto e artigo são dinâmicas.
  • Cada URL precisa de title, description, og:image, canonical próprios.
  • Compartilhamento social (Open Graph/Twitter) precisa ser consistente.

Solução: useHead ou useSeoMeta.

3.2 Exemplo de useHead​

useHead({
title: () => product.value?.name,
meta: [
{ name: 'description', content: () => product.value?.description },
{ property: 'og:title', content: () => product.value?.name },
{ property: 'og:image', content: () => product.value?.image },
],
link: [
{
rel: 'canonical',
href: () => `https://example.com/products/${product.value?.id}`,
},
],
});

Boas práticas:

  1. Passar valores como função (() => ...) para reagir a mudancas de dados.
  2. Cobrir estrutura SEO completa: title, description, OG, canonical.
  3. Em 404, usar noindex, nofollow.

3.3 Variante compacta com useSeoMeta​

useSeoMeta({
title: () => product.value?.name,
description: () => product.value?.description,
ogTitle: () => product.value?.name,
ogDescription: () => product.value?.description,
ogImage: () => product.value?.image,
});

4. Caso prático 1: SEO para rotas dinâmicas​

4.1 Contexto​

Cenário de e-commerce com muitas páginas SKU (/products/[id]).

Desafios:

  • Muitas URLs dinâmicas
  • SEO único por URL
  • Tratamento correto de 404
  • Evitar conteúdo duplicado

4.2 Estratégia​

  1. Preload no servidor (lazy: false, server: true)
  2. Lancar 404 com createError
  3. Gerar meta e canonical dinamicamente
const { data: product, error } = await useFetch(`/api/products/${id}`, {
key: `product-${id}`,
lazy: false,
server: true,
});

if (error.value || !product.value) {
throw createError({ statusCode: 404, statusMessage: 'Product not found' });
}

useSeoMeta({
title: () => `${product.value?.name} - Product`,
description: () => product.value?.description,
ogTitle: () => product.value?.name,
ogDescription: () => product.value?.description,
ogImage: () => product.value?.image,
});

4.3 Resultado​

Antes:

  • Crawler via conteúdo incompleto
  • Varias páginas com metadata repetida
  • 404 inconsistente

Depois:

  • HTML SSR completo para crawler
  • Metadata único por rota
  • Tratamento de erro consistente é seguro para SEO

5. Caso prático 2: Otimização de performance​

5.1 Problema​

SSR aumenta carga de servidor. Sem otimização, latencia e custo sobem.

5.2 Estratégias​

  1. Request deduplication
const { data } = await useFetch('/api/product/123', {
key: 'product-123',
});
  1. Cache em servidor (Nitro)
export default defineCachedEventHandler(
async (event) => {
return await getProductsFromDB();
},
{
maxAge: 60 * 60,
swr: true,
},
);
  1. Separar SSR/CSR
  • Páginas críticas para SEO: SSR
  • Páginas internas sem indexação: CSR
  1. Critical CSS e estratégia de assets
  • Priorizar CSS above-the-fold
  • Carregar recursos não críticos depois

5.3 Impacto​

Antes:

  • Alta carga no servidor
  • Requests duplicados
  • Sem estratégia de cache

Depois:

  • Melhor tempo de resposta
  • Menos pressao em backend/DB
  • Performance mais estável sob carga

6. Respostas curtas de entrevista​

6.1 useFetch / useAsyncData​

Na carga inicial eu uso useFetch com key, lazy: false e server: true para garantir SSR completo e HTML útil para indexação.

6.2 Meta tags dinâmicas​

Eu uso useHead/useSeoMeta com valores em função para atualizar metadados conforme os dados, incluindo OG e canonical.

6.3 Performance​

Eu combino deduplication, cache no servidor e split SSR/CSR para reduzir TTFB sem perder qualidade de SEO.


7. Best practices​

7.1 Data fetching​

  1. Definir key sempre.
  2. Escolher lazy de acordo com objetivo SEO.
  3. Tratar erros (404/5xx) explicitamente.

7.2 SEO Meta​

  1. Valores funcionais para updates reativos.
  2. Estrutura completa (title/description/OG/canonical).
  3. Proteger páginas de erro com noindex, nofollow.

7.3 Performance​

  1. Aplicar cache no servidor.
  2. Usar SSR apenas onde SEO precisa.
  3. Reduzir custo de render com estratégia de CSS/assets.

8. Resumo para entrevista​

Em Nuxt 3 eu implementei data fetching SSR e gestao dinâmica de SEO Meta para cobrir dois objetivos: indexação correta e experiência rápida. Para isso eu combinei preload no servidor, metadados por rota e otimizações com deduplication, cache e separação SSR/CSR.

Pontos-chave:

  • ✅ Uso correto de useFetch/useAsyncData
  • ✅ Gestao dinâmica com useHead/useSeoMeta
  • ✅ SEO para rotas dinâmicas
  • ✅ Performance em cenários reais