Edge Computing × Frontend: Haz que tu sitio sea ultrarrápido con Cloudflare Workers/Vercel Edge
¿Por qué el frontend necesita Edge Computing?
En las arquitecturas tradicionales, las solicitudes de los usuarios deben atravesar la mitad de internet para llegar a los servidores de origen:
Usuario (Tokio) → Nodo CDN (Tokio, solo caché estático) → Origen (EE.UU. Oeste, cómputo dinámico)
Latencia: ~10ms ~150ms cruzando el Pacífico ~50ms cómputo
Total: ~210ms
La computación en el borde lleva la lógica de cómputo al nodo más cercano al usuario:
Usuario (Tokio) → Nodo Edge (Tokio, cómputo directo)
Latencia: ~10ms ~5ms cómputo local
Total: ~15ms
14 veces menos latencia — ese es el poder de la computación en el borde.
Comparación de las tres principales plataformas Edge
| Dimensión | Cloudflare Workers | Vercel Edge Functions | Deno Deploy |
|---|---|---|---|
| Entorno de ejecución | V8 Isolate | V8 Isolate | Deno |
| Inicio en frío | <5ms | <5ms | ~20ms |
| Nodos globales | 300+ ciudades | 30+ regiones | 35+ regiones |
| Plan gratuito | 100K solicitudes/día | Facturación por proyecto | 1M solicitudes/mes |
| Lenguajes | JS/TS/WASM | JS/TS | JS/TS/WASM |
| Límite de tamaño del paquete | 10MB | 4MB | Sin límite |
| Almacenamiento KV | Workers KV | Edge Config | Deno KV |
| Integración con frameworks | Agnóstico al framework | Integración profunda con Next.js | Fresh |
Escenario práctico 1: Enrutamiento geográfico
Dirige a los usuarios a contenido diferente según su ubicación geográfica con latencia cero:
// Cloudflare Workers
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const country = request.cf?.country as string;
const routes: Record<string, string> = {
CN: 'https://cn.example.com',
JP: 'https://jp.example.com',
US: 'https://us.example.com',
EU: 'https://eu.example.com',
};
const target = routes[country] || routes['US'];
return Response.redirect(target, 302);
},
};
Versión para Vercel Edge
// middleware.ts (Next.js)
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const country = request.geo?.country || 'US';
const localeMap: Record<string, string> = {
CN: '/zh-CN',
TW: '/zh-TW',
JP: '/ja',
};
const locale = localeMap[country] || '/en';
if (!request.nextUrl.pathname.startsWith(locale)) {
return NextResponse.redirect(new URL(locale, request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!api|_next/static).*)'],
};
Escenario práctico 2: Pruebas A/B
Dirige el tráfico en la capa edge — sin necesidad de JavaScript del lado del cliente:
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// Read or assign experiment group
let variant = getCookie(request, 'ab-variant');
if (!variant) {
variant = Math.random() < 0.5 ? 'control' : 'experiment';
}
// Read corresponding version HTML from KV
const html = await env.KV.get(`landing:${variant}`);
const response = new Response(html, {
headers: { 'Content-Type': 'text/html' },
});
// Set cookie to maintain group consistency
response.headers.append(
'Set-Cookie',
`ab-variant=${variant}; Path=/; Max-Age=86400`
);
return response;
},
};
function getCookie(request: Request, name: string): string | null {
const cookies = request.headers.get('Cookie') || '';
const match = cookies.match(new RegExp(`${name}=([^;]+)`));
return match ? match[1] : null;
}
Comparación de ventajas
| Enfoque | FOUC | Impacto en rendimiento | Amigable con SEO | Complejidad |
|---|---|---|---|---|
| Enrutamiento con JS del cliente | ❌ Parpadea | Reduce LCP | ❌ | Baja |
| Enrutamiento del lado del servidor | ✅ Sin parpadeo | Aumenta la latencia | ✅ | Media |
| Enrutamiento Edge | ✅ Sin parpadeo | Casi sin impacto | ✅ | Media |
Escenario práctico 3: Contenido personalizado
export default {
async fetch(request: Request, env: Env) {
const country = request.cf?.country;
const timezone = request.cf?.timezone;
// Display local time based on timezone
const localTime = new Date().toLocaleTimeString('en-US', {
timeZone: timezone,
});
// Display local currency based on country
const currencyMap: Record<string, { symbol: string; rate: number }> = {
CN: { symbol: '¥', rate: 7.2 },
JP: { symbol: '¥', rate: 150 },
US: { symbol: '$', rate: 1 },
};
const currency = currencyMap[country] || currencyMap['US'];
// Fetch base page and inject personalized data
const response = await fetch(request);
const html = await response.text();
const personalized = html
.replace('{{LOCAL_TIME}}', localTime)
.replace('{{CURRENCY_SYMBOL}}', currency.symbol);
return new Response(personalized, {
headers: response.headers,
});
},
};
Escenario práctico 4: Agregación y caché de API
Agrega múltiples APIs en la capa edge para reducir las solicitudes del cliente:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const cache = caches.default;
const cacheKey = new Request(request.url);
// 1. Check edge cache
let response = await cache.match(cacheKey);
if (response) return response;
// 2. Parallel requests to multiple APIs
const [user, posts, notifications] = await Promise.all([
fetch('https://api.example.com/user', { headers: request.headers }),
fetch('https://api.example.com/posts?limit=10'),
fetch('https://api.example.com/notifications/unread'),
]);
// 3. Aggregate responses
const data = {
user: await user.json(),
posts: await posts.json(),
notifications: await notifications.json(),
};
response = new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=60',
},
});
// 4. Write to edge cache
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
},
};
Almacenamiento KV Edge: Base de datos global de baja latencia
Cloudflare Workers KV
// Write
await env.KV.put('user:123:preferences', JSON.stringify({
theme: 'dark',
language: 'zh-CN',
}));
// Read (eventually consistent, ~60s global sync)
const prefs = await env.KV.get('user:123:preferences', 'json');
// List
const list = await env.KV.list({ prefix: 'user:123:' });
Vercel Edge Config
import { get } from '@vercel/edge-config';
// Read configuration (globally synced, ~1s latency)
const flags = await get('feature-flags');
if (flags?.newHomepage) {
return newHomepage();
}
return oldHomepage();
Migración desde arquitecturas tradicionales
Ruta de migración
Fase 1: Activos estáticos en CDN (1-2 días)
→ HTML/JS/CSS/imágenes → Caché edge de CDN
→ Las solicitudes dinámicas siguen yendo al origen
Fase 2: Middleware edge (1-2 semanas)
→ Redirecciones, pruebas A/B, enrutamiento geográfico → Edge
→ La lógica de negocio sigue en el origen
Fase 3: Edge-ificación de APIs ligeras (2-4 semanas)
→ APIs de lectura intensiva → Edge + KV
→ Las operaciones de escritura siguen yendo al origen
Fase 4: Edge-ificación de lógica pesada (según demanda)
→ SSR → Edge SSR
→ Respuestas de streaming → Edge Streaming
Consideraciones
| Limitación | Descripción | Mitigación |
|---|---|---|
| Sin APIs de Node.js | No hay fs, path, etc. |
Usar Web APIs en su lugar |
| Límite de tiempo de ejecución | Workers: 30ms (gratuito) / 30s (de pago) | Dividir tareas largas |
| Límite de tamaño del paquete | Workers: 10MB | Tree-shaking, WASM |
| Sin estado global | Isolate aislado por solicitud | Persistir con KV/D1 |
| Inicio en frío no nulo | V8 Isolate ~5ms | Calentar rutas clave |
Benchmarks de rendimiento
El sitio ToolsKu utiliza la arquitectura CDN de Cloudflare + exportación estática:
Métrica SSR Tradicional Edge Estático
TTFB 200-500ms 10-30ms
LCP 1.5-3s 0.3-0.8s
Latencia global P50 150ms 15ms
Latencia global P99 800ms 50ms
Resumen
La computación en el borde es el arma definitiva para la optimización del rendimiento frontend — no se trata de ahorrar 50ms en el servidor, sino de reducir 150ms de latencia de red a 5ms. En 2026, si tus páginas dinámicas aún no se ejecutan en el edge, estás desperdiciando más de 100ms del tiempo de espera de tus usuarios. Desde el enrutamiento geográfico y las pruebas A/B hasta la agregación de APIs, la computación en el borde da a los desarrolladores frontend el superpoder de "desplegar código en más de 300 ciudades de todo el mundo".
Prueba estas herramientas que se ejecutan en tu navegador — no requieren registro →