Edge Computing × Frontend : Rendez votre site ultra-rapide avec Cloudflare Workers/Vercel Edge
Pourquoi le frontend a-t-il besoin de l'Edge Computing ?
Dans les architectures traditionnelles, les requêtes des utilisateurs doivent traverser la moitié d'Internet pour atteindre les serveurs d'origine :
Utilisateur (Tokyo) → Nœud CDN (Tokyo, cache statique uniquement) → Origine (Ouest des États-Unis, calcul dynamique)
Latence : ~10ms ~150ms transpacifique ~50ms calcul
Total : ~210ms
L'informatique en périphérie rapproche la logique de calcul du nœud le plus proche de l'utilisateur :
Utilisateur (Tokyo) → Nœud Edge (Tokyo, calcul direct)
Latence : ~10ms ~5ms calcul local
Total : ~15ms
Réduction de la latence par 14 — c'est la puissance de l'informatique en périphérie.
Comparaison des trois principales plateformes Edge
| Dimension | Cloudflare Workers | Vercel Edge Functions | Deno Deploy |
|---|---|---|---|
| Runtime | V8 Isolate | V8 Isolate | Deno |
| Démarrage à froid | <5ms | <5ms | ~20ms |
| Nœuds globaux | 300+ villes | 30+ régions | 35+ régions |
| Offre gratuite | 100K requêtes/jour | Facturation par projet | 1M requêtes/mois |
| Langages | JS/TS/WASM | JS/TS | JS/TS/WASM |
| Limite de taille du bundle | 10Mo | 4Mo | Illimitée |
| Stockage KV | Workers KV | Edge Config | Deno KV |
| Intégration framework | Agnostique au framework | Intégration profonde avec Next.js | Fresh |
Scénario pratique 1 : Routage géographique
Dirigez les utilisateurs vers différents contenus en fonction de leur localisation géographique avec une latence nulle :
// 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);
},
};
Version 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).*)'],
};
Scénario pratique 2 : Tests A/B
Routez le trafic au niveau edge — sans JavaScript côté client :
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;
}
Comparaison des avantages
| Approche | FOUC | Impact sur les performances | Compatible SEO | Complexité |
|---|---|---|---|---|
| Routage JS côté client | ❌ Clignotement | Dégrade le LCP | ❌ | Faible |
| Routage côté serveur | ✅ Pas de clignotement | Augmente la latence | ✅ | Moyenne |
| Routage Edge | ✅ Pas de clignotement | Quasi nul | ✅ | Moyenne |
Scénario pratique 3 : Contenu personnalisé
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,
});
},
};
Scénario pratique 4 : Agrégation et cache d'API
Agrégez plusieurs API au niveau edge pour réduire les requêtes client :
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;
},
};
Stockage KV Edge : Base de données mondiale à faible latence
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();
Migration depuis les architectures traditionnelles
Chemin de migration
Phase 1 : Actifs statiques sur CDN (1-2 jours)
→ HTML/JS/CSS/images → Cache edge du CDN
→ Les requêtes dynamiques vont toujours à l'origine
Phase 2 : Middleware edge (1-2 semaines)
→ Redirections, tests A/B, routage géographique → Edge
→ La logique métier reste à l'origine
Phase 3 : Edge-ification des API légères (2-4 semaines)
→ API à forte lecture → Edge + KV
→ Les opérations d'écriture vont toujours à l'origine
Phase 4 : Edge-ification de la logique lourde (à la demande)
→ SSR → Edge SSR
→ Réponses en streaming → Edge Streaming
Considérations
| Limitation | Description | Atténuation |
|---|---|---|
| Pas d'API Node.js | Pas de fs, path, etc. |
Utiliser les API Web à la place |
| Limite de temps d'exécution | Workers : 30ms (gratuit) / 30s (payant) | Diviser les tâches longues |
| Limite de taille du bundle | Workers : 10Mo | Tree-shaking, WASM |
| Pas d'état global | Isolate isolé par requête | Persister avec KV/D1 |
| Démarrage à froid non nul | V8 Isolate ~5ms | Préchauffer les routes clés |
Benchmarks de performance
Le site ToolsKu utilise l'architecture CDN Cloudflare + export statique :
Métrique SSR Traditionnel Edge Statique
TTFB 200-500ms 10-30ms
LCP 1.5-3s 0.3-0.8s
Latence globale P50 150ms 15ms
Latence globale P99 800ms 50ms
Résumé
L'informatique en périphérie est l'arme ultime pour l'optimisation des performances frontend — il ne s'agit pas d'économiser 50ms sur le serveur, mais de réduire 150ms de latence réseau à 5ms. En 2026, si vos pages dynamiques ne tournent pas encore sur le edge, vous gaspillez plus de 100ms du temps d'attente de vos utilisateurs. Du routage géographique et des tests A/B à l'agrégation d'API, l'informatique en périphérie donne aux développeurs frontend le super-pouvoir de « déployer du code dans plus de 300 villes à travers le monde ».
Essayez ces outils exécutés localement dans le navigateur — aucune inscription requise →