Optimización rendimiento web freelance Valencia: Core Web Vitals, LCP, FID, CLS guía completa 2025
Optimización rendimiento web freelance Valencia: Core Web Vitals, LCP, FID, CLS guía completa 2025
Introducción: La velocidad es el nuevo SEO
En 2025, Google penaliza los sitios lentos. No es opcional: es obligatorio.
Como desarrollador freelance, he optimizado docenas de sitios. He visto cómo un sitio que carga en 1 segundo convierte 3x más que uno que carga en 5 segundos.
En este artículo te doy mi guía completa de optimización: técnicas probadas, herramientas y casos reales que han mejorado el rendimiento de mis clientes en 200-300%.
Los Core Web Vitals: Las métricas que importan
1. LCP (Largest Contentful Paint) - Contenido principal
¿Qué mide? Cuándo aparece el contenido más grande visible.
Objetivo: <2.5 segundos
Cómo mejorar LCP
Problema común: Imágenes no optimizadas
<!-- ❌ Antes: Imagen sin optimizar -->
<img src="hero-image.jpg" alt="Hero" style="width: 100%; height: 400px;">
<!-- ✅ Después: Imagen optimizada -->
<img
src="hero-image.webp"
alt="Hero"
width="1200"
height="400"
loading="eager"
decoding="async"
style="width: 100%; height: auto;"
>
Técnicas avanzadas para LCP:
// Preload recursos críticos
<link rel="preload" href="/hero-image.webp" as="image" type="image/webp">
<link rel="preload" href="/hero-font.woff2" as="font" type="font/woff2" crossorigin>
// Critical CSS inline
<style>
.hero { background: #fff; font-family: 'Inter'; }
.hero h1 { font-size: 3rem; color: #000; }
</style>
2. FID (First Input Delay) - Interactividad
¿Qué mide? Retraso en respuesta a la primera interacción.
Objetivo: <100 milisegundos
Causas comunes de FID malo
- JavaScript bloqueante
- Tareas largas del main thread
- No dividir código (code splitting)
// ❌ Código bloqueante
import { heavyLibrary } from 'heavy-lib'; // Se carga todo al inicio
import { unusedComponent } from './components'; // Nunca se usa
// ✅ Code splitting inteligente
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
const [showHeavy, setShowHeavy] = useState(false);
return (
<div>
<button onClick={() => setShowHeavy(true)}>
Load heavy component
</button>
{showHeavy && <HeavyComponent />}
</div>
);
}
3. CLS (Cumulative Layout Shift) - Estabilidad visual
¿Qué mide? Cambios inesperados en el layout.
Objetivo: <0.1
Cómo eliminar CLS
/* ❌ Dimensiones no especificadas */
<img src="image.jpg" alt="Image">
/* ✅ Dimensiones explícitas */
img {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
}
/* O usando CSS moderno */
.image-container {
aspect-ratio: 16 / 9;
background: #f0f0f0;
}
Prevención de CLS en fuentes:
/* Evitar FOIT (Flash of Invisible Text) */
@font-face {
font-family: 'Inter';
font-display: swap; /* Muestra fallback inmediatamente */
src: url('/fonts/inter.woff2') format('woff2');
}
Stack de optimización: Herramientas que uso
1. Next.js + Vercel (Mi combo favorito)
// next.config.js - Configuración optimizada
/** @type {import('next').NextConfig} */
const nextConfig = {
// Imágenes automáticas optimizadas
images: {
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
},
// Compresión automática
compress: true,
// Bundle analyzer
webpack: (config, { dev }) => {
if (!dev) {
config.optimization.splitChunks.chunks = 'all';
}
return config;
},
// Headers de cache
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
],
},
{
source: '/static/(.*)',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
],
},
];
},
};
module.exports = nextConfig;
2. Astro: Rendimiento por defecto
// astro.config.mjs
import { defineConfig } from 'astro/config';
import image from '@astrojs/image';
import compress from 'astro-compress';
export default defineConfig({
site: 'https://tu-sitio.com',
integrations: [
image({
serviceEntryPoint: '@astrojs/image/sharp',
}),
compress(),
],
// Optimizaciones automáticas
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['astro'],
},
},
},
},
},
});
3. CDN y Edge Computing
// vercel.json - Configuración edge
{
"functions": {
"api/**/*.js": {
"runtime": "@vercel/node"
}
},
"rewrites": [
{
"source": "/api/(.*)",
"destination": "/api/$1"
}
],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}
Técnicas avanzadas de optimización
1. Resource Hints: Cargar lo que necesitas antes
<!-- Preload recursos críticos -->
<link rel="preload" href="/css/critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<link rel="preload" href="/js/main.js" as="script">
<!-- Preconnect a dominios externos -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- DNS prefetch para recursos no críticos -->
<link rel="dns-prefetch" href="//cdn.example.com">
2. Critical CSS: CSS que importa primero
// Extraer CSS crítico automáticamente
import critical from 'critical';
await critical.generate({
inline: true,
base: 'dist/',
src: 'index.html',
target: 'index.html',
width: 1300,
height: 900,
});
3. Service Worker para cache avanzado
// sw.js - Service Worker para cache
const CACHE_NAME = 'v1';
const urlsToCache = [
'/',
'/css/styles.css',
'/js/main.js',
'/images/logo.webp',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
4. Optimización de fuentes web
/* Sistema de fuentes optimizado */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/inter-regular.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Font loading strategy */
.font-loading {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
5. Lazy loading inteligente
// Intersection Observer para lazy loading
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = img.dataset.src!;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
Casos reales: Optimizaciones que funcionaron
Caso 1: E-commerce B2B - De 8s a 1.2s
Cliente: Plataforma de pedidos mayoristas
Problemas iniciales:
- LCP: 6.8s
- FID: 450ms
- CLS: 0.35
- Lighthouse: 45/100
Soluciones implementadas:
- Imágenes optimizadas: WebP + lazy loading
- Code splitting: Componentes críticos separados
- CDN global: Assets servidos desde edge
- Database optimization: Queries optimizadas
Resultados:
- ⏱️ LCP: 1.2s (-82%)
- ⚡ FID: 45ms (-90%)
- 📏 CLS: 0.05 (-86%)
- 📊 Lighthouse: 95/100 (+110%)
- 💰 Conversion rate: +65%
Caso 2: Blog corporativo - De 4.5s a 0.8s
Cliente: Blog de empresa tecnológica
Problemas iniciales:
- Múltiples plugins WordPress lentos
- Imágenes sin optimizar
- CSS/JS no minificado
- Sin cache adecuado
Migración a headless:
- WordPress → CMS headless
- Next.js + SSG
- Imágenes automáticas optimizadas
- Critical CSS inline
Resultados:
- ⏱️ Carga inicial: 0.8s (-82%)
- 📈 SEO: Posición 1 en keywords principales
- 👥 Tráfico orgánico: +180%
- 💰 ROI: Recuperado en 3 meses
Caso 3: App React - De 12s a 2.1s
Cliente: Dashboard de analytics
Problemas iniciales:
- Bundle de 8MB
- Sin code splitting
- Librerías pesadas innecesarias
- Re-renders excesivos
Optimizaciones:
- Tree shaking: Eliminar código muerto
- Dynamic imports: Componentes bajo demanda
- Memoización: React.memo + useMemo
- Virtualización: React Window para listas grandes
Resultados:
- 📦 Bundle size: 2.1MB (-74%)
- ⚡ First paint: 1.1s (-91%)
- 🔄 Interactions: Fluido 60fps
- 😊 UX Score: +300%
Herramientas de medición y monitoreo
Lighthouse CI en GitHub Actions
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push, pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Serve and test
run: |
npx serve dist -l 3000 &
sleep 3
npx lighthouse http://localhost:3000 \
--output=json \
--output-path=./lighthouse-results.json \
--chrome-flags="--headless --disable-gpu"
- name: Comment PR
uses: dorny/test-reporter@v1
with:
name: Lighthouse Results
path: 'lighthouse-results.json'
reporter: 'lighthouse-json'
Web Vitals monitoring en producción
// lib/web-vitals.ts
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
export function reportWebVitals(metric) {
// Enviar a analytics
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('event', metric.name, {
value: Math.round(metric.value),
event_category: 'Web Vitals',
event_label: metric.id,
non_interaction: true,
});
}
// Console log en desarrollo
if (process.env.NODE_ENV === 'development') {
console.log(metric);
}
}
// Inicializar medición
if (typeof window !== 'undefined') {
getCLS(reportWebVitals);
getFID(reportWebVitals);
getFCP(reportWebVitals);
getLCP(reportWebVitals);
getTTFB(reportWebVitals);
}
Real User Monitoring (RUM)
// Monitoring con Sentry
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.nextRouterInstrumentation,
}),
],
// Performance monitoring
enableTracing: true,
});
Checklist de optimización: 30 días para 100/100
Semana 1: Fundamentos
- Auditar sitio actual con Lighthouse
- Implementar compresión (gzip/brotli)
- Optimizar imágenes (WebP, responsive)
- Minificar CSS/JS
- Configurar cache headers
Semana 2: Core Web Vitals
- Mejorar LCP (<2.5s)
- Optimizar FID (<100ms)
- Eliminar CLS (<0.1)
- Critical CSS inline
- Resource hints (preload, preconnect)
Semana 3: Avanzado
- Code splitting
- Lazy loading
- CDN implementation
- Service Worker cache
- Database optimization
Semana 4: Monitoreo
- Lighthouse CI setup
- Web Vitals tracking
- Real User Monitoring
- Alertas de performance
- Reportes automáticos
Conclusión: Rendimiento como ventaja competitiva
Los sitios lentos pierden clientes. Los rápidos los ganan.
En 2025, el rendimiento no es técnico: es negocio. Cada segundo cuenta.
¿Necesitas optimizar tu sitio web? Contáctame y alcancemos juntos ese 100/100 en Lighthouse.
Preguntas frecuentes (FAQ)
¿Cuál es el objetivo de LCP/FID/CLS para 2025?
LCP < 2.5s, FID < 100ms (o INP < 200ms) y CLS < 0.1. Apunta a 100/100 en Lighthouse en páginas clave.
¿Qué impacto tiene en SEO?
Core Web Vitals son señal de ranking. Mejorar rendimiento suele aumentar conversiones y posicionamiento.
¿Qué optimizaciones dan mayor impacto rápido?
Imágenes WebP/AVIF + lazyload, critical CSS, preload de fuentes, code splitting y cache/CDN.
¿Cómo monitorizo en producción?
Web Vitals + Google Analytics (gtag) y, si puedes, RUM o Sentry Performance para métricas reales de usuarios.
Recursos relacionados
- Astro vs Next.js: ¿Cuál elegir para tu proyecto web en 2025?
- Herramientas de IA para desarrollo web: Stack completo 2025
- WordPress Headless con React y Next.js: La guía completa 2025
Publicado el 17 de diciembre de 2025 por Adrián Pozo Esteban - Desarrollador Full Stack Freelance especializado en optimización de rendimiento web y Core Web Vitals en Valencia, España

