跳至內容
THE GUILD
0%
服務 產品 招募 關於我們 網誌 常見問題 聯繫我們
前端開發者邊緣計算完全指南2026

邊緣計算已從基礎設施關注點轉變為關鍵的前端開發技能。2026年,隨著開發者部署在全球用戶幾毫秒內執行的程式碼,前端和後端之間的界限繼續模糊。本綜合指南涵蓋了前端開發者需要了解的關於邊緣計算的一切——從基本概念到生產就緒的實現。

無論您是最佳化現有應用程式還是設計新系統,理解邊緣計算對於建構現代高效能Web應用程式至關重要。

對於前端開發者來說,邊緣計算是什麼?

邊緣計算將計算和資料儲存帶到更接近資料來源的位置。對於前端開發者來說,這意味著在靠近用戶的分散式伺服器上執行伺服器端邏輯,而不是在集中的資料中心。

傳統架構 vs 邊緣架構

傳統架構:

東京用戶 → CDN(靜態檔案) → 維吉尼亞來源伺服器 → 資料庫

                              500ms以上延遲

邊緣架構:

東京用戶 → 東京邊緣伺服器 → 來源(如需要)

               50ms延遲
               (動態內容在邊緣渲染)

為什麼前端開發者需要邊緣技能

Web平台的演變從根本上改變了「前端」的含義:

  1. 伺服器元件:React Server Components、Astro等技術模糊了用戶端-伺服器邊界
  2. 邊緣渲染:為個人化動態內容在邊緣進行SSR和SSG
  3. API路由:前端框架現在包含後端功能
  4. 即時功能:WebSockets和串流的邊緣原生解決方案

邊緣平台:前端開發者指南

多個平台提供邊緣計算功能。了解它們的優勢有助於選擇正確的工具。

Vercel邊緣函數

Vercel邊緣函數執行在Vercel的邊緣網絡上,由V8隔離驅動。非常適合Next.js應用程式。

主要特性:

  • 亞毫秒冷啟動
  • 串流回應
  • 預設全球部署
  • 原生Next.js整合

基本邊緣函數:

// app/api/hello/route.ts
export const runtime = 'edge';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const name = searchParams.get('name') || 'World';

  return new Response(`Hello, ${name}!`, {
    headers: {
      'content-type': 'text/plain',
    },
  });
}

邊緣中介軟件:

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 從邊緣取得用戶國家
  const country = request.geo?.country || 'US';

  // 基於位置個人化
  const response = NextResponse.next();
  response.headers.set('x-user-country', country);

  // 邊緣A/B測試
  const bucket = Math.random() < 0.5 ? 'control' : 'experiment';
  response.cookies.set('ab-bucket', bucket);

  return response;
}

export const config = {
  matcher: ['/((?!_next/static|favicon.ico).*)'],
};

Cloudflare Workers

Cloudflare Workers提供最成熟的邊緣計算平台,具有廣泛的API。

主要特性:

  • 全球200多個邊緣位置
  • 邊緣資料的KV儲存
  • 狀態應用的Durable Objects
  • 邊緣機器學習的Workers AI

基本Worker:

// src/index.js
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    if (url.pathname === '/api/data') {
      // 從邊緣KV儲存取得
      const cachedData = await env.MY_KV.get('data-key', 'json');

      if (cachedData) {
        return Response.json(cachedData);
      }

      // 從來源取得並緩存
      const response = await fetch('https://api.origin.com/data');
      const data = await response.json();

      // 緩存1小時
      ctx.waitUntil(env.MY_KV.put('data-key', JSON.stringify(data), {
        expirationTtl: 3600
      }));

      return Response.json(data);
    }

    return new Response('Not Found', { status: 404 });
  }
};

Wrangler設定:

# wrangler.toml
name = "my-edge-app"
main = "src/index.js"
compatibility_date = "2026-01-01"

[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"

[vars]
ENVIRONMENT = "production"

Deno Deploy

Deno Deploy提供一流TypeScript支援和Web標準API的邊緣計算。

主要特性:

  • 開箱即用的TypeScript
  • Web標準API
  • 內建KV資料庫
  • GitHub整合

Deno邊緣函數:

// main.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts";

const kv = await Deno.openKv();

serve(async (request: Request) => {
  const url = new URL(request.url);

  if (url.pathname === "/api/counter") {
    // 邊緣原子遞增
    const key = ["counters", "visits"];
    const result = await kv.atomic()
      .sum(key, 1n)
      .commit();

    const entry = await kv.get(key);

    return Response.json({
      count: entry.value?.toString() || "0"
    });
  }

  return new Response("Hello from the Edge!");
}, { port: 8000 });

AWS CloudFront Functions & Lambda@Edge

AWS提供兩種功能不同的邊緣計算選項。

CloudFront Functions(輕量級):

function handler(event) {
  var request = event.request;
  var headers = request.headers;

  // 在邊緣新增安全性標頭
  var response = {
    statusCode: 200,
    statusDescription: 'OK',
    headers: {
      'strict-transport-security': { value: 'max-age=31536000' },
      'content-security-policy': { value: "default-src 'self'" },
      'x-content-type-options': { value: 'nosniff' },
      'x-frame-options': { value: 'DENY' }
    }
  };

  return response;
}

Lambda@Edge(完整運算):

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;

  // 在邊緣進行個人化
  const userAgent = request.headers['user-agent'][0].value;
  const isMobile = /Mobile|Android/.test(userAgent);

  if (isMobile) {
    request.uri = request.uri.replace(/\.html$/, '.mobile.html');
  }

  return request;
};

常見邊緣計算模式

前端開發者經常在邊緣實現這些模式。

模式1:邊緣個人化

無需來源往返即可提供個人化內容:

// 個人化的Vercel邊緣中介軟件
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  const response = NextResponse.next();

  // 取得個人化上下文
  const country = request.geo?.country || 'US';
  const city = request.geo?.city || 'Unknown';
  const userAgent = request.headers.get('user-agent') || '';

  // 確定內容變體
  const isMobile = /Mobile|Android|iPhone/.test(userAgent);
  const locale = getPreferredLocale(request.headers.get('accept-language'));
  const currency = getCurrencyForCountry(country);

  // 為下游使用設定標頭
  response.headers.set('x-personalization', JSON.stringify({
    country,
    city,
    isMobile,
    locale,
    currency
  }));

  // 重寫到個人化內容
  if (request.nextUrl.pathname === '/') {
    return NextResponse.rewrite(
      new URL(`/${locale}/home?currency=${currency}`, request.url),
      { headers: response.headers }
    );
  }

  return response;
}

function getPreferredLocale(acceptLanguage: string | null): string {
  if (!acceptLanguage) return 'en';

  const locales = ['en', 'ja', 'de', 'fr', 'es', 'zh'];
  const preferred = acceptLanguage.split(',')[0].split('-')[0];

  return locales.includes(preferred) ? preferred : 'en';
}

function getCurrencyForCountry(country: string): string {
  const currencyMap: Record<string, string> = {
    US: 'USD',
    JP: 'JPY',
    GB: 'GBP',
    DE: 'EUR',
    MY: 'MYR'
  };

  return currencyMap[country] || 'USD';
}

模式2:邊緣A/B測試

實現無用戶端閃爍的一致A/B測試:

// A/B測試的Cloudflare Worker
interface Env {
  EXPERIMENTS_KV: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // 取得或分配實驗桶
    const cookies = parseCookies(request.headers.get('cookie'));
    let bucket = cookies['ab-experiment'];

    if (!bucket) {
      // 將新用戶分配到實驗
      const experiment = await env.EXPERIMENTS_KV.get('homepage-v2', 'json') as {
        variants: { id: string; weight: number }[];
      };

      bucket = assignVariant(experiment.variants);
    }

    // 重寫到實驗變體
    if (url.pathname === '/') {
      const variantUrl = new URL(url);
      variantUrl.pathname = `/experiments/${bucket}/index.html`;

      const response = await fetch(variantUrl);
      const modifiedResponse = new Response(response.body, response);

      // 設定Cookie以保持一致體驗
      modifiedResponse.headers.append(
        'Set-Cookie',
        `ab-experiment=${bucket}; Path=/; Max-Age=2592000; SameSite=Lax`
      );

      // 為分析追蹤分配
      await trackExperimentAssignment(bucket, request);

      return modifiedResponse;
    }

    return fetch(request);
  }
};

function assignVariant(variants: { id: string; weight: number }[]): string {
  const random = Math.random();
  let cumulative = 0;

  for (const variant of variants) {
    cumulative += variant.weight;
    if (random < cumulative) {
      return variant.id;
    }
  }

  return variants[0].id;
}

模式3:邊緣端包含(ESI)

從邊緣緩存的片段組合頁面:

// ESI風格組合的邊緣函數
export async function onRequest(context: EventContext) {
  const url = new URL(context.request.url);

  if (url.pathname.startsWith('/product/')) {
    // 並行取得頁面元件
    const [header, productContent, recommendations, footer] = await Promise.all([
      fetchCachedFragment('/fragments/header'),
      fetchProductContent(url.pathname),
      fetchPersonalizedRecommendations(context.request),
      fetchCachedFragment('/fragments/footer')
    ]);

    // 組合最終HTML
    const html = `
      <!DOCTYPE html>
      <html>
        <head>
          <title>${productContent.title}</title>
        </head>
        <body>
          ${header}
          <main>${productContent.html}</main>
          <aside>${recommendations}</aside>
          ${footer}
        </body>
      </html>
    `;

    return new Response(html, {
      headers: {
        'content-type': 'text/html',
        'cache-control': 'public, max-age=60, stale-while-revalidate=300'
      }
    });
  }

  return context.next();
}

async function fetchCachedFragment(path: string): Promise<string> {
  // 片段在邊緣緩存1小時
  const response = await fetch(`https://origin.com${path}`, {
    cf: { cacheTtl: 3600 }
  });
  return response.text();
}

async function fetchPersonalizedRecommendations(request: Request): Promise<string> {
  // 基於Cookie/地理位置的個人化內容
  const userId = getCookie(request, 'user_id');
  const country = request.cf?.country || 'US';

  const response = await fetch(
    `https://api.origin.com/recommendations?user=${userId}&country=${country}`
  );
  const data = await response.json();

  return renderRecommendations(data);
}

模式4:邊緣認證

在請求到達來源之前在邊緣驗證認證:

// 邊緣JWT驗證
import { jwtVerify } from 'jose';

interface Env {
  JWT_SECRET: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // 跳過公共路由的認證
    if (isPublicRoute(url.pathname)) {
      return fetch(request);
    }

    // 提取並驗證JWT
    const authHeader = request.headers.get('authorization');

    if (!authHeader?.startsWith('Bearer ')) {
      return new Response('Unauthorized', { status: 401 });
    }

    const token = authHeader.slice(7);

    try {
      const secret = new TextEncoder().encode(env.JWT_SECRET);
      const { payload } = await jwtVerify(token, secret);

      // 向請求新增用戶上下文
      const modifiedHeaders = new Headers(request.headers);
      modifiedHeaders.set('x-user-id', payload.sub as string);
      modifiedHeaders.set('x-user-role', payload.role as string);

      const modifiedRequest = new Request(request, {
        headers: modifiedHeaders
      });

      return fetch(modifiedRequest);
    } catch (error) {
      return new Response('Invalid token', { status: 401 });
    }
  }
};

function isPublicRoute(pathname: string): boolean {
  const publicRoutes = ['/login', '/register', '/api/health', '/public'];
  return publicRoutes.some(route => pathname.startsWith(route));
}

模式5:使用Stale-While-Revalidate的智慧緩存

在邊緣實現智慧緩存策略:

// 具有智慧緩存的Cloudflare Worker
interface Env {
  CACHE_KV: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const cacheKey = new URL(request.url).pathname;

    // 嘗試從邊緣緩存取得
    const cached = await env.CACHE_KV.getWithMetadata(cacheKey, 'json');

    if (cached.value) {
      const metadata = cached.metadata as { expires: number; stale: number };
      const now = Date.now();

      if (now < metadata.expires) {
        // 新鮮緩存命中
        return Response.json(cached.value, {
          headers: { 'x-cache': 'HIT' }
        });
      }

      if (now < metadata.stale) {
        // Stale-while-revalidate
        ctx.waitUntil(revalidateCache(request, env, cacheKey));

        return Response.json(cached.value, {
          headers: { 'x-cache': 'STALE' }
        });
      }
    }

    // 緩存未命中 - 從來源取得
    return revalidateCache(request, env, cacheKey);
  }
};

async function revalidateCache(
  request: Request,
  env: Env,
  cacheKey: string
): Promise<Response> {
  const response = await fetch(`https://api.origin.com${cacheKey}`);
  const data = await response.json();

  // 儲存並附上TTL中繼資料
  const now = Date.now();
  await env.CACHE_KV.put(cacheKey, JSON.stringify(data), {
    metadata: {
      expires: now + 60000,      // 新鮮1分鐘
      stale: now + 300000        // 過期後5分鐘內仍可用
    }
  });

  return Response.json(data, {
    headers: { 'x-cache': 'MISS' }
  });
}

邊緣資料儲存

邊緣計算需要邊緣原生的資料儲存解決方案。

Cloudflare KV

針對讀取密集工作負載最佳化的鍵值儲存:

// 邊緣的KV操作
interface Env {
  USER_PREFERENCES: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const userId = url.searchParams.get('userId');

    if (request.method === 'GET') {
      // 讀取用戶偏好設定
      const prefs = await env.USER_PREFERENCES.get(`user:${userId}`, 'json');
      return Response.json(prefs || { theme: 'light', language: 'en' });
    }

    if (request.method === 'POST') {
      // 更新偏好設定
      const body = await request.json();

      await env.USER_PREFERENCES.put(`user:${userId}`, JSON.stringify(body), {
        expirationTtl: 86400 * 30  // 30天
      });

      return Response.json({ success: true });
    }

    return new Response('Method not allowed', { status: 405 });
  }
};

Cloudflare Durable Objects

為即時應用程式提供邊緣的狀態運算:

// 用於即時協作的Durable Object
export class DocumentRoom {
  state: DurableObjectState;
  sessions: Map<WebSocket, { userId: string }>;
  document: string;

  constructor(state: DurableObjectState) {
    this.state = state;
    this.sessions = new Map();
    this.document = '';
  }

  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/websocket') {
      // 處理WebSocket升級
      const pair = new WebSocketPair();
      const [client, server] = Object.values(pair);

      const userId = url.searchParams.get('userId') || 'anonymous';
      this.handleSession(server, userId);

      return new Response(null, { status: 101, webSocket: client });
    }

    return new Response('Not found', { status: 404 });
  }

  handleSession(webSocket: WebSocket, userId: string) {
    webSocket.accept();
    this.sessions.set(webSocket, { userId });

    // 傳送目前文件狀態
    webSocket.send(JSON.stringify({
      type: 'sync',
      document: this.document,
      users: Array.from(this.sessions.values()).map(s => s.userId)
    }));

    webSocket.addEventListener('message', (event) => {
      const message = JSON.parse(event.data as string);

      if (message.type === 'edit') {
        // 套用編輯並廣播
        this.document = applyEdit(this.document, message.edit);

        this.broadcast({
          type: 'edit',
          userId,
          edit: message.edit
        }, webSocket);
      }
    });

    webSocket.addEventListener('close', () => {
      this.sessions.delete(webSocket);
      this.broadcast({ type: 'user_left', userId });
    });
  }

  broadcast(message: object, exclude?: WebSocket) {
    const json = JSON.stringify(message);

    for (const [ws] of this.sessions) {
      if (ws !== exclude) {
        ws.send(json);
      }
    }
  }
}

Deno KV

Deno Deploy內建的鍵值資料庫:

// 用於邊緣資料的Deno KV
const kv = await Deno.openKv();

// 原子交易
async function transferBalance(fromId: string, toId: string, amount: number) {
  const fromKey = ["balances", fromId];
  const toKey = ["balances", toId];

  // 原子檢查並更新
  let success = false;

  while (!success) {
    const fromEntry = await kv.get<number>(fromKey);
    const toEntry = await kv.get<number>(toKey);

    const fromBalance = fromEntry.value || 0;
    const toBalance = toEntry.value || 0;

    if (fromBalance < amount) {
      throw new Error("Insufficient balance");
    }

    const result = await kv.atomic()
      .check(fromEntry)
      .check(toEntry)
      .set(fromKey, fromBalance - amount)
      .set(toKey, toBalance + amount)
      .commit();

    success = result.ok;
  }

  return { success: true };
}

// 次要索引
async function addUser(user: { id: string; email: string; name: string }) {
  await kv.atomic()
    .set(["users", user.id], user)
    .set(["users_by_email", user.email], user.id)
    .commit();
}

async function getUserByEmail(email: string) {
  const idEntry = await kv.get<string>(["users_by_email", email]);
  if (!idEntry.value) return null;

  const userEntry = await kv.get(["users", idEntry.value]);
  return userEntry.value;
}

邊緣效能最佳化

透過以下最佳化技術最大化邊緣計算的效益。

串流回應

在內容產生的同時進行串流傳輸:

// 來自邊緣的串流回應
export async function onRequest(context: EventContext): Promise<Response> {
  const { readable, writable } = new TransformStream();
  const writer = writable.getWriter();
  const encoder = new TextEncoder();

  // 立即開始串流
  context.waitUntil((async () => {
    // 立即寫入HTML head
    await writer.write(encoder.encode(`
      <!DOCTYPE html>
      <html>
        <head><title>Streaming Page</title></head>
        <body>
          <header>Loading content...</header>
    `));

    // 主要內容一準備好就進行串流
    const mainContent = await fetchMainContent();
    await writer.write(encoder.encode(`
          <main>${mainContent}</main>
    `));

    // 串流側邊欄
    const sidebarContent = await fetchSidebarContent();
    await writer.write(encoder.encode(`
          <aside>${sidebarContent}</aside>
    `));

    // 完成頁面
    await writer.write(encoder.encode(`
        </body>
      </html>
    `));

    await writer.close();
  })());

  return new Response(readable, {
    headers: { 'content-type': 'text/html' }
  });
}

請求合併

防止緩存未命中時發生驚群效應:

// 邊緣的請求合併
const inflightRequests = new Map<string, Promise<Response>>();

export async function onRequest(context: EventContext): Promise<Response> {
  const cacheKey = context.request.url;

  // 檢查請求是否已在處理中
  const inflight = inflightRequests.get(cacheKey);
  if (inflight) {
    // 等待現有請求
    const response = await inflight;
    return response.clone();
  }

  // 發起新請求並儲存Promise
  const fetchPromise = fetchWithCache(context.request);
  inflightRequests.set(cacheKey, fetchPromise);

  try {
    const response = await fetchPromise;
    return response;
  } finally {
    // 完成後清理
    inflightRequests.delete(cacheKey);
  }
}

async function fetchWithCache(request: Request): Promise<Response> {
  const cache = caches.default;

  let response = await cache.match(request);

  if (!response) {
    response = await fetch(request);

    // 緩存成功的回應
    if (response.ok) {
      const cloned = response.clone();
      cloned.headers.set('Cache-Control', 'public, max-age=60');
      await cache.put(request, cloned);
    }
  }

  return response;
}

邊緣最佳化圖片

在邊緣轉換並最佳化圖片:

// 邊緣的圖片最佳化
export async function onRequest(context: EventContext): Promise<Response> {
  const url = new URL(context.request.url);

  if (!url.pathname.startsWith('/images/')) {
    return context.next();
  }

  // 解析最佳化參數
  const width = parseInt(url.searchParams.get('w') || '0');
  const quality = parseInt(url.searchParams.get('q') || '80');
  const format = url.searchParams.get('f') || 'auto';

  // 根據Accept標頭決定最佳格式
  const accept = context.request.headers.get('accept') || '';
  const outputFormat = format === 'auto'
    ? accept.includes('image/avif') ? 'avif'
    : accept.includes('image/webp') ? 'webp'
    : 'jpeg'
    : format;

  // 使用Cloudflare Image Resizing
  const imageUrl = `https://origin.com${url.pathname}`;

  const response = await fetch(imageUrl, {
    cf: {
      image: {
        width: width || undefined,
        quality,
        format: outputFormat
      }
    }
  });

  // 新增緩存標頭
  const headers = new Headers(response.headers);
  headers.set('Cache-Control', 'public, max-age=31536000, immutable');
  headers.set('Vary', 'Accept');

  return new Response(response.body, {
    status: response.status,
    headers
  });
}

測試邊緣函數

邊緣程式碼的完整測試策略。

本機開發

在本機執行邊緣函數:

// 用於本機Cloudflare Workers測試的Miniflare
import { Miniflare } from 'miniflare';

const mf = new Miniflare({
  script: `
    export default {
      async fetch(request, env) {
        return new Response('Hello from edge!');
      }
    }
  `,
  modules: true,
  kvNamespaces: ['MY_KV'],
});

const response = await mf.dispatchFetch('http://localhost/');
console.log(await response.text()); // "Hello from edge!"

單元測試

獨立測試邊緣函數:

// 邊緣函數的Jest測試
import { unstable_dev } from 'wrangler';

describe('Edge API', () => {
  let worker: any;

  beforeAll(async () => {
    worker = await unstable_dev('src/index.ts', {
      experimental: { disableExperimentalWarning: true }
    });
  });

  afterAll(async () => {
    await worker.stop();
  });

  test('returns personalized greeting', async () => {
    const response = await worker.fetch('/api/greet?name=World');
    const text = await response.text();

    expect(response.status).toBe(200);
    expect(text).toBe('Hello, World!');
  });

  test('handles missing parameters', async () => {
    const response = await worker.fetch('/api/greet');
    const text = await response.text();

    expect(text).toBe('Hello, Guest!');
  });
});

整合測試

使用真實依賴項測試邊緣函數:

// 使用KV儲存的整合測試
describe('Edge Cache', () => {
  let mf: Miniflare;
  let kv: any;

  beforeEach(async () => {
    mf = new Miniflare({
      scriptPath: './src/index.ts',
      modules: true,
      kvNamespaces: ['CACHE']
    });

    kv = await mf.getKVNamespace('CACHE');
  });

  test('caches API responses', async () => {
    // 第一次請求 - 緩存未命中
    const response1 = await mf.dispatchFetch('http://localhost/api/data');
    expect(response1.headers.get('x-cache')).toBe('MISS');

    // 第二次請求 - 緩存命中
    const response2 = await mf.dispatchFetch('http://localhost/api/data');
    expect(response2.headers.get('x-cache')).toBe('HIT');

    // 驗證資料是否在KV中
    const cached = await kv.get('/api/data', 'json');
    expect(cached).toBeDefined();
  });
});

安全性考量

邊緣計算帶來獨特的安全性挑戰。

邊緣的輸入驗證

驗證並清理所有輸入:

// 輸入驗證中介軟件
export async function middleware(request: NextRequest) {
  const url = new URL(request.url);

  // 驗證查詢參數
  for (const [key, value] of url.searchParams) {
    if (!isValidParameter(key, value)) {
      return new Response('Bad Request', { status: 400 });
    }
  }

  // 驗證POST/PUT的請求主體
  if (['POST', 'PUT', 'PATCH'].includes(request.method)) {
    try {
      const body = await request.json();

      if (!validateRequestBody(body)) {
        return new Response('Invalid request body', { status: 400 });
      }
    } catch {
      return new Response('Invalid JSON', { status: 400 });
    }
  }

  return NextResponse.next();
}

function isValidParameter(key: string, value: string): boolean {
  // 長度限制
  if (key.length > 64 || value.length > 1024) return false;

  // 字元驗證
  if (!/^[\w-]+$/.test(key)) return false;

  // SQL注入樣式
  const sqlPatterns = /('|"|;|--|\b(SELECT|INSERT|UPDATE|DELETE|DROP)\b)/i;
  if (sqlPatterns.test(value)) return false;

  return true;
}

速率限制

在邊緣實現速率限制:

// 使用Cloudflare KV的邊緣速率限制
interface Env {
  RATE_LIMITS: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const clientIP = request.headers.get('cf-connecting-ip') || 'unknown';
    const windowKey = `ratelimit:${clientIP}:${Math.floor(Date.now() / 60000)}`;

    // 取得目前計數
    const current = await env.RATE_LIMITS.get(windowKey);
    const count = current ? parseInt(current) : 0;

    // 檢查限制(每分鐘100次請求)
    if (count >= 100) {
      return new Response('Rate limit exceeded', {
        status: 429,
        headers: {
          'Retry-After': '60',
          'X-RateLimit-Limit': '100',
          'X-RateLimit-Remaining': '0'
        }
      });
    }

    // 遞增計數器
    await env.RATE_LIMITS.put(windowKey, String(count + 1), {
      expirationTtl: 120  // 2分鐘TTL
    });

    // 處理請求
    const response = await fetch(request);
    const modifiedResponse = new Response(response.body, response);

    modifiedResponse.headers.set('X-RateLimit-Limit', '100');
    modifiedResponse.headers.set('X-RateLimit-Remaining', String(99 - count));

    return modifiedResponse;
  }
};

結論:擁抱邊緣優先開發

邊緣計算代表了Web開發的未來。隨著應用程式要求更低延遲、更高個人化和全球覆蓋,邊緣計算提供了交付卓越用戶體驗的架構。

關鍵要點

  1. 選擇正確的平台:Next.js用Vercel,複雜工作負載用Cloudflare,TypeScript優先開發用Deno
  2. 邊緣優先思考:從一開始就考慮邊緣計算來設計應用程式
  3. 利用邊緣資料:為狀態邊緣應用使用KV儲存和Durable Objects
  4. 最佳化串流傳輸:串流回應以提高感知效能
  5. 徹底測試:本地開發和整合測試至關重要

邊緣計算的未來

在2026年及以後,邊緣計算將繼續發展:

  • 邊緣AI:在邊緣節點執行機器學習推理
  • WebAssembly擴展:更多語言編譯到邊緣平台執行
  • 更深整合:前端框架和邊緣執行時更緊密耦合

需要邊緣開發專業知識?

建構邊緣原生應用需要分散式系統、效能最佳化和現代Web架構的專業知識。我們的離岸開發團隊專門使用邊緣計算技術建構高效能Web應用程式。

探索Web系統開發 了解SaaS開發


準備好將應用程式帶到邊緣了嗎?Web效能的未來是分散式的。

您面臨什麼邊緣計算挑戰?分享您的經驗和問題。


來源