Edge Computing × Фронтенд: Сделайте свой сайт молниеносным с Cloudflare Workers/Vercel Edge
Зачем фронтенду Edge Computing?
В традиционных архитектурах запросы пользователей должны преодолеть половину интернета, чтобы достичь серверов-источников:
Пользователь (Токио) → Узел CDN (Токио, только статический кэш) → Источник (Запад США, динамические вычисления)
Задержка: ~10мс ~150мс через Тихий океан ~50мс вычисления
Итого: ~210мс
Периферийные вычисления переносят логику вычислений на узел, ближайший к пользователю:
Пользователь (Токио) → Edge-узел (Токио, прямые вычисления)
Задержка: ~10мс ~5мс локальные вычисления
Итого: ~15мс
Снижение задержки в 14 раз — вот сила периферийных вычислений.
Сравнение трёх основных Edge-платформ
| Параметр | Cloudflare Workers | Vercel Edge Functions | Deno Deploy |
|---|---|---|---|
| Среда выполнения | V8 Isolate | V8 Isolate | Deno |
| Холодный старт | <5мс | <5мс | ~20мс |
| Глобальные узлы | 300+ городов | 30+ регионов | 35+ регионов |
| Бесплатный тариф | 100K запросов/день | Биллинг по проектам | 1M запросов/месяц |
| Языки | JS/TS/WASM | JS/TS | JS/TS/WASM |
| Лимит размера бандла | 10МБ | 4МБ | Без ограничений |
| KV-хранилище | Workers KV | Edge Config | Deno KV |
| Интеграция с фреймворками | Независим от фреймворка | Глубокая интеграция с Next.js | Fresh |
Практический сценарий 1: Гео-маршрутизация
Направляйте пользователей к разному контенту на основе географического положения с нулевой задержкой:
// 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);
},
};
Версия для 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).*)'],
};
Практический сценарий 2: A/B-тестирование
Маршрутизируйте трафик на уровне edge — без клиентского JavaScript:
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;
}
Сравнение преимуществ
| Подход | FOUC | Влияние на производительность | SEO-совместимость | Сложность |
|---|---|---|---|---|
| Маршрутизация на клиенте | ❌ Мигает | Снижает LCP | ❌ | Низкая |
| Серверная маршрутизация | ✅ Без мигания | Увеличивает задержку | ✅ | Средняя |
| Edge-маршрутизация | ✅ Без мигания | Практически без влияния | ✅ | Средняя |
Практический сценарий 3: Персонализированный контент
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,
});
},
};
Практический сценарий 4: Агрегация и кэширование API
Агрегируйте несколько API на уровне edge, чтобы уменьшить количество клиентских запросов:
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;
},
};
Edge KV-хранилище: Глобальная база данных с низкой задержкой
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();
Миграция с традиционных архитектур
Путь миграции
Этап 1: Статические активы на CDN (1-2 дня)
→ HTML/JS/CSS/изображения → Edge-кэш CDN
→ Динамические запросы всё ещё идут к источнику
Этап 2: Edge-промежуточный слой (1-2 недели)
→ Перенаправления, A/B-тесты, гео-маршрутизация → Edge
→ Бизнес-логика остаётся на источнике
Этап 3: Перенос лёгких API на edge (2-4 недели)
→ API с интенсивным чтением → Edge + KV
→ Операции записи всё ещё идут к источнику
Этап 4: Перенос тяжёлой логики на edge (по требованию)
→ SSR → Edge SSR
→ Стриминг-ответы → Edge Streaming
Соображения
| Ограничение | Описание | Смягчение |
|---|---|---|
| Нет API Node.js | Нет fs, path и т.д. |
Использовать Web API вместо этого |
| Ограничение времени выполнения | Workers: 30мс (бесплатно) / 30с (платно) | Разбивать длинные задачи |
| Лимит размера бандла | Workers: 10МБ | Tree-shaking, WASM |
| Нет глобального состояния | Изолированный isolate на запрос | Сохранять в KV/D1 |
| Холодный старт не нулевой | V8 Isolate ~5мс | Прогревать ключевые маршруты |
Тесты производительности
Сайт ToolsKu использует архитектуру Cloudflare CDN + статический экспорт:
Метрика Традиционный SSR Edge Статика
TTFB 200-500мс 10-30мс
LCP 1.5-3с 0.3-0.8с
Глобальная P50 150мс 15мс
Глобальная P99 800мс 50мс
Итоги
Периферийные вычисления — это абсолютное оружие для оптимизации производительности фронтенда. Речь не о том, чтобы сэкономить 50мс на сервере, а о том, чтобы сократить 150мс сетевой задержки до 5мс. В 2026 году, если ваши динамические страницы ещё не работают на edge, вы тратите более 100мс времени ожидания ваших пользователей. От гео-маршрутизации и A/B-тестирования до агрегации API — периферийные вычисления дают фронтенд-разработчикам суперсилу «разворачивать код в 300+ городах по всему миру».
Попробуйте эти локальные браузерные инструменты — регистрация не требуется →