Edge Functions #

Edge Functions 概述 #

Edge Functions 是在 Vercel 边缘网络上运行的函数:

text
┌──────────────────────────────────────────────────────┐
│                                                      │
│  传统服务器架构                                      │
│  用户 ──────────────────▶ 中央服务器                │
│        (距离远,延迟高)                              │
│                                                      │
│  边缘计算架构                                        │
│  用户 ───▶ 最近的边缘节点 ───▶ 执行函数             │
│        (就近访问,低延迟)                            │
│                                                      │
└──────────────────────────────────────────────────────┘

Edge vs Serverless #

特性 Edge Functions Serverless Functions
启动时间 毫秒级 秒级(冷启动)
执行位置 边缘节点 特定区域
延迟 极低 较低
运行时 V8/Edge Runtime Node.js
执行时间限制 30秒 60秒+
包大小 1MB 50MB

Edge Runtime #

运行时限制 #

text
┌─────────────────────────────────────────┐
│        Edge Runtime 限制                 │
├─────────────────────────────────────────┤
│  执行时间    →  30 秒                    │
│  内存        →  128 MB                   │
│  代码大小    →  1 MB                     │
│  请求体大小  →  4 MB                     │
└─────────────────────────────────────────┘

支持的 API #

text
Web 标准 API:
├── fetch
├── Request / Response
├── Headers
├── URL / URLSearchParams
├── Crypto
├── TextEncoder / TextDecoder
└── setTimeout / setInterval

不支持的 API #

text
Node.js 特有 API:
├── fs
├── path
├── crypto (Node.js 版本)
├── buffer
└── process

创建 Edge Function #

Next.js API Route #

typescript
import { NextRequest, NextResponse } from 'next/server'

export const runtime = 'edge'

export async function GET(request: NextRequest) {
  return NextResponse.json({
    message: 'Hello from Edge!',
    geo: request.geo,
  })
}

文件位置 #

text
app/
└── api/
    └── hello/
        └── route.ts

独立 Edge Function #

typescript
export const config = {
  runtime: 'edge',
}

export default async function handler(request: Request) {
  return new Response('Hello from Edge!', {
    headers: {
      'content-type': 'text/plain',
    },
  })
}

中间件 #

创建中间件 #

typescript
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const response = NextResponse.next()
  
  response.headers.set('x-custom-header', 'value')
  
  return response
}

export const config = {
  matcher: '/api/:path*',
}

中间件位置 #

text
项目根目录/
└── middleware.ts

匹配路径 #

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

地理路由 #

获取地理位置 #

typescript
import { NextRequest, NextResponse } from 'next/server'

export const runtime = 'edge'

export async function GET(request: NextRequest) {
  const geo = request.geo
  
  return NextResponse.json({
    city: geo?.city,
    country: geo?.country,
    region: geo?.region,
    latitude: geo?.latitude,
    longitude: geo?.longitude,
  })
}

地理位置字段 #

字段 说明 示例
city 城市 “San Francisco”
country 国家代码 “US”
region 地区代码 “CA”
latitude 纬度 “37.7749”
longitude 经度 “-122.4194”

基于地理位置的重定向 #

typescript
import { NextRequest, NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
  const country = request.geo?.country || 'US'
  
  if (country === 'CN') {
    return NextResponse.redirect(new URL('/cn', request.url))
  }
  
  if (country === 'JP') {
    return NextResponse.redirect(new URL('/jp', request.url))
  }
  
  return NextResponse.next()
}

A/B 测试 #

简单 A/B 测试 #

typescript
import { NextRequest, NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
  const variant = Math.random() > 0.5 ? 'A' : 'B'
  
  const response = NextResponse.next()
  response.headers.set('x-variant', variant)
  
  return response
}

持久化变体 #

typescript
import { NextRequest, NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
  let variant = request.cookies.get('variant')?.value
  
  if (!variant) {
    variant = Math.random() > 0.5 ? 'A' : 'B'
  }
  
  const response = NextResponse.next()
  response.cookies.set('variant', variant, { maxAge: 60 * 60 * 24 * 30 })
  
  return response
}

身份验证 #

JWT 验证 #

typescript
import { NextRequest, NextResponse } from 'next/server'
import { jwtVerify } from 'jose'

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('token')?.value
  
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  
  try {
    const secret = new TextEncoder().encode(process.env.JWT_SECRET)
    await jwtVerify(token, secret)
    return NextResponse.next()
  } catch {
    return NextResponse.redirect(new URL('/login', request.url))
  }
}

export const config = {
  matcher: '/dashboard/:path*',
}

基本认证 #

typescript
import { NextRequest, NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
  const authHeader = request.headers.get('authorization')
  
  if (!authHeader) {
    return new NextResponse('Authentication required', {
      status: 401,
      headers: {
        'WWW-Authenticate': 'Basic realm="Secure Area"',
      },
    })
  }
  
  const [username, password] = atob(authHeader.split(' ')[1]).split(':')
  
  if (username !== 'admin' || password !== 'secret') {
    return new NextResponse('Invalid credentials', { status: 401 })
  }
  
  return NextResponse.next()
}

缓存控制 #

边缘缓存 #

typescript
import { NextResponse } from 'next/server'

export const runtime = 'edge'

export async function GET() {
  const data = await fetch('https://api.example.com/data', {
    cache: 'force-cache',
  }).then(res => res.json())
  
  return NextResponse.json(data, {
    headers: {
      'Cache-Control': 'public, max-age=3600, s-maxage=3600',
    },
  })
}

动态响应 #

typescript
import { NextResponse } from 'next/server'

export const runtime = 'edge'

export async function GET() {
  return NextResponse.json(
    { time: new Date().toISOString() },
    {
      headers: {
        'Cache-Control': 'no-store',
      },
    }
  )
}

请求处理 #

读取请求体 #

typescript
export const runtime = 'edge'

export async function POST(request: Request) {
  const body = await request.json()
  
  return Response.json({ received: body })
}

读取表单数据 #

typescript
export const runtime = 'edge'

export async function POST(request: Request) {
  const formData = await request.formData()
  const name = formData.get('name')
  const file = formData.get('file')
  
  return Response.json({ name, file: file?.name })
}

流式响应 #

typescript
export const runtime = 'edge'

export async function GET() {
  const encoder = new TextEncoder()
  
  const stream = new ReadableStream({
    async start(controller) {
      for (let i = 0; i < 10; i++) {
        controller.enqueue(encoder.encode(`data: ${i}\n\n`))
        await new Promise(resolve => setTimeout(resolve, 1000))
      }
      controller.close()
    },
  })
  
  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
    },
  })
}

配置选项 #

vercel.json 配置 #

json
{
  "functions": {
    "app/api/**/*.ts": {
      "runtime": "edge"
    }
  }
}

区域配置 #

json
{
  "functions": {
    "app/api/**/*.ts": {
      "runtime": "edge",
      "regions": ["hkg1", "sfo1"]
    }
  }
}

调试 #

本地调试 #

bash
vercel dev

日志输出 #

typescript
export const runtime = 'edge'

export async function GET() {
  console.log('Edge function executed')
  return Response.json({ status: 'ok' })
}

查看日志 #

bash
vercel logs --follow

最佳实践 #

性能优化 #

text
┌─────────────────────────────────────────┐
│        Edge Functions 最佳实践           │
├─────────────────────────────────────────┤
│  ✓ 保持代码精简                         │
│  ✓ 避免大型依赖                         │
│  ✓ 使用缓存减少请求                     │
│  ✓ 合理设置超时                         │
│  ✓ 处理错误情况                         │
└─────────────────────────────────────────┘

错误处理 #

typescript
export const runtime = 'edge'

export async function GET() {
  try {
    const response = await fetch('https://api.example.com/data')
    
    if (!response.ok) {
      throw new Error('API request failed')
    }
    
    const data = await response.json()
    return Response.json(data)
  } catch (error) {
    return Response.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    )
  }
}

下一步 #

了解 Edge Functions 后,接下来学习 Serverless Functions 探索更多后端能力!

最后更新:2026-03-28