[Lv2] Implementación SSR: Data Fetching y gestión de SEO Meta
En un proyecto Nuxt 3: implementar carga de datos en SSR y gestión dinámica de SEO Meta para que los buscadores indexen correctamente rutas dinámicas.
1. Eje de respuesta en entrevista
- Estrategia de data fetching: usar
useFetch/useAsyncDatapara precargar en servidor y entregar HTML completo para SEO. - Meta tags dinámicos: usar
useHeadouseSeoMetapara generar metadatos por recurso. - Rendimiento: aplicar request deduplication, cache en servidor y separar claramente páginas SSR y CSR.
2. Uso correcto de useFetch / useAsyncData
2.1 Por qué es importante el data fetching en SSR
Escenario típico:
- Las rutas dinámicas (por ejemplo
/products/[id]) necesitan datos de API. - Si solo se carga en cliente, el crawler puede ver contenido incompleto.
- Objetivo: enviar HTML renderizado con datos completos desde servidor.
Solución: usar useFetch o useAsyncData en Nuxt 3.
2.2 Ejemplo base de useFetch
Ruta: pages/products/[id].vue
// uso base
const { data: product } = await useFetch(`/api/products/${route.params.id}`);
Opciones importantes:
| Opcion | Objetivo | Default |
|---|---|---|
key | Clave única para deduplicar requests | auto |
lazy | Carga diferida (no bloquea SSR) | false |
server | Ejecutar en servidor | true |
default | Valor fallback | null |
transform | Transformar respuesta antes de consumir | - |
2.3 Ejemplo 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 los datos
server: true, // garantiza ejecución en servidor
default: () => ({
id: null,
name: '',
description: '',
image: '',
}),
transform: (data: any) => {
// normalizacion de datos
return {
...data,
formattedPrice: formatPrice(data.price),
};
},
});
Por qué importan estas opciones:
key- Permite deduplication de requests.
- Mismo key -> un request efectivo.
lazy: false- El servidor renderiza después de tener datos.
- El crawler recibe contenido final.
server: true- El fetch corre en ruta SSR.
- Evita depender solo del cliente.
2.4 useAsyncData vs useFetch
| Criterio | useFetch | useAsyncData |
|---|---|---|
| Uso principal | Llamadas API | Cualquier operación async |
| Comodidad | URL/header integrados | Logica manual |
| Escenario común | HTTP data fetching | DB queries, agregaciones, archivos |
// useFetch: centrado en API
const { data } = await useFetch('/api/products/123');
// useAsyncData: lógica async personalizada
const { data } = await useAsyncData('products', async () => {
const result = await someAsyncOperation();
return result;
});
2.5 $fetch vs useFetch
Regla corta para entrevista:
$fetchpara acciones de usuario (click, submit, refresh).useFetchpara carga inicial con sincronización SSR/Hydration.
$fetch características:
- Cliente HTTP puro (
ofetch) - No transfiere estado SSR
- Usarlo directo en
setup()puede causar double fetch
useFetch características:
- Combina
useAsyncData+$fetch - Compatible con hydration
- Entrega
data,pending,error,refresh
Comparación:
| Punto | useFetch | $fetch |
|---|---|---|
| Transferencia SSR | Si | No |
| Retorno | Refs reactivas | Promise con datos puros |
| Uso principal | Carga inicial de página | Operaciones por evento |
// correcto: carga inicial
const { data } = await useFetch('/api/user');
// correcto: accion de usuario
const submitForm = async () => {
await $fetch('/api/submit', { method: 'POST', body: form });
};
// evitar: setup + $fetch directo
const data = await $fetch('/api/user');
3. Gestión SEO Meta con useHead
3.1 Por que se necesitan meta tags dinámicos
Escenario típico:
- Páginas de producto o articulo son dinámicas.
- Cada URL necesita su propio
title,description,og:image, canonical. - Compartir en redes (Open Graph/Twitter) debe ser consistente.
Solución: useHead o useSeoMeta.
3.2 Ejemplo con 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}`,
},
],
});
Buenas prácticas:
- Pasar valores como funciones (
() => ...) para reaccionar a cambios de datos. - Incluir estructura SEO completa: title, description, OG, canonical.
- Para 404, marcar
noindex, nofollow.
3.3 Variante compacta con 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áctico 1: SEO para rutas dinámicas
4.1 Contexto
Escenario e-commerce con muchas páginas SKU (/products/[id]).
Retos:
- Muchas URLs dinámicas
- SEO único por URL
- Manejo correcto de 404
- Evitar contenido duplicado
4.2 Estrategia
- Precargar en servidor (
lazy: false,server: true) - Lanzar 404 con
createError - Generar meta y canonical en forma dinámica
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:
- El crawler ve contenido incompleto
- Varias páginas comparten meta
- 404 inconsistente
Después:
- HTML SSR completo para crawler
- Meta único por ruta
- Manejo de error consistente y seguro para SEO
5. Caso práctico 2: Optimización de rendimiento
5.1 Problema
SSR sube carga en servidor. Sin optimización, aumentan latencia y costo.
5.2 Estrategias
- Request deduplication
const { data } = await useFetch('/api/product/123', {
key: 'product-123',
});
- Cache en servidor (Nitro)
export default defineCachedEventHandler(
async (event) => {
return await getProductsFromDB();
},
{
maxAge: 60 * 60,
swr: true,
},
);
- Separar SSR/CSR
- Páginas críticas para SEO: SSR
- Páginas internas sin indexación: CSR
- Critical CSS y estrategia de assets
- Priorizar CSS above-the-fold
- Cargar recursos no criticos después
5.3 Impacto
Antes:
- Alta carga de servidor
- Requests repetidos
- Sin politica de cache
Después:
- Mejor tiempo de respuesta
- Menor presion en backend/DB
- Mejor estabilidad bajo carga
6. Respuestas de entrevista (versión corta)
6.1 useFetch / useAsyncData
Para carga inicial uso
useFetchconkey,lazy: falseyserver: truepara asegurar SSR completo y HTML útil para buscadores.
6.2 Meta tags dinámicos
Uso
useHead/useSeoMetacon valores en función para que los metadatos se actualicen con los datos, incluyendo OG y canonical.
6.3 Rendimiento
Combino deduplication, cache en servidor y división SSR/CSR para reducir TTFB y mantener calidad SEO.
7. Best practices
7.1 Data fetching
- Definir siempre
key. - Elegir
lazysegún necesidad SEO. - Tratar errores (404/5xx) de forma explicita.
7.2 SEO Meta
- Valores funcionales para updates reactivos.
- Estructura completa (title/description/OG/canonical).
- Proteger páginas de error con
noindex, nofollow.
7.3 Rendimiento
- Aplicar cache en servidor.
- Usar SSR solo donde SEO lo necesita.
- Reducir costo de render con estrategia de CSS/assets.
8. Resumen para entrevista
En Nuxt 3 implementé data fetching SSR y gestión dinámica de SEO Meta para cubrir dos objetivos: indexación correcta y experiencia rápida. Para eso combine precarga en servidor, metadatos por ruta y optimizaciones como deduplication, cache y separación SSR/CSR.
Puntos clave:
- ✅ Uso correcto de
useFetch/useAsyncData - ✅ Gestión dinámica con
useHead/useSeoMeta - ✅ SEO para rutas dinámicas
- ✅ Rendimiento en escenarios reales