Edge Runtime 和 Node.js Runtime 有什么区别?如何为页面、路由或中间件选择运行时?
Edge Runtime vs Node.js Runtime
1. 基本概念
Node.js Runtime:
- 完整的 Node.js 环境
- 支持所有 Node.js API
- 更大的内存和计算资源
- 更长的执行时间
Edge Runtime:
- 基于 Web APIs 的轻量级运行时
- 更快的冷启动
- 更小的内存占用
- 更短的执行时间
2. 功能对比
// Node.js Runtime - 支持所有 Node.js API
// app/api/node-route/route.js
export async function GET() {
// ✅ 支持文件系统操作
const fs = await import('fs/promises')
const data = await fs.readFile('file.txt', 'utf8')
// ✅ 支持子进程
const { spawn } = await import('child_process')
// ✅ 支持所有 npm 包
const bcrypt = await import('bcrypt')
return NextResponse.json({ data })
}
// Edge Runtime - 只支持 Web APIs
// app/api/edge-route/route.js
export const runtime = 'edge'
export async function GET() {
// ✅ 支持 Web APIs
const response = await fetch('https://api.example.com')
const data = await response.json()
// ✅ 支持 URL 和 Headers
const url = new URL(request.url)
const headers = new Headers()
// ❌ 不支持文件系统
// const fs = await import('fs/promises') // 错误
// ❌ 不支持子进程
// const { spawn } = await import('child_process') // 错误
return NextResponse.json({ data })
}
运行时选择
1. 页面级别配置
// app/edge-page/page.js
export const runtime = 'edge'
export default function EdgePage() {
return (
<div>
<h1>Edge Runtime Page</h1>
<p>This page runs on Edge Runtime</p>
</div>
)
}
2. API 路由配置
// app/api/edge-api/route.js
export const runtime = 'edge'
export async function GET(request) {
const { searchParams } = new URL(request.url)
const query = searchParams.get('q')
// 使用 Web APIs
const response = await fetch(`https://api.example.com/search?q=${query}`)
const data = await response.json()
return NextResponse.json(data)
}
3. 中间件配置
// middleware.js
export const config = {
matcher: ['/api/:path*', '/dashboard/:path*'],
}
export function middleware(request) {
// 中间件默认运行在 Edge Runtime
const url = request.nextUrl.clone()
// 重定向逻辑
if (url.pathname === '/old-path') {
url.pathname = '/new-path'
return NextResponse.redirect(url)
}
// 添加自定义头部
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return response
}
实际应用场景
1. Edge Runtime 适用场景
// app/api/geolocation/route.js
export const runtime = 'edge'
export async function GET(request) {
const { searchParams } = new URL(request.url)
const lat = searchParams.get('lat')
const lng = searchParams.get('lng')
// 地理位置处理
const response = await fetch(
`https://api.geoapify.com/v1/geocode/reverse?lat=${lat}&lon=${lng}&apiKey=${process.env.GEO_API_KEY}`
)
const data = await response.json()
return NextResponse.json({
location: data.features[0]?.properties,
})
}
// app/api/weather/route.js
export const runtime = 'edge'
export async function GET(request) {
const { searchParams } = new URL(request.url)
const city = searchParams.get('city')
// 天气 API 调用
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}`
)
const data = await response.json()
return NextResponse.json({
temperature: data.main.temp,
description: data.weather[0].description,
})
}
2. Node.js Runtime 适用场景
// app/api/file-upload/route.js
export const runtime = 'nodejs'
export async function POST(request) {
const formData = await request.formData()
const file = formData.get('file')
// 文件系统操作
const fs = await import('fs/promises')
const path = await import('path')
const buffer = await file.arrayBuffer()
const filename = `${Date.now()}-${file.name}`
const filepath = path.join(process.cwd(), 'uploads', filename)
await fs.writeFile(filepath, Buffer.from(buffer))
return NextResponse.json({ filename })
}
// app/api/database/route.js
export const runtime = 'nodejs'
export async function GET() {
// 数据库操作
const { PrismaClient } = await import('@prisma/client')
const prisma = new PrismaClient()
const users = await prisma.user.findMany()
return NextResponse.json({ users })
}
性能对比
1. 冷启动时间
// Edge Runtime - 更快的冷启动
// app/api/fast-api/route.js
export const runtime = 'edge'
export async function GET() {
// 冷启动时间: ~50ms
const start = Date.now()
const response = await fetch('https://api.example.com')
const data = await response.json()
const duration = Date.now() - start
return NextResponse.json({
data,
duration: `${duration}ms`,
})
}
// Node.js Runtime - 较慢的冷启动
// app/api/slow-api/route.js
export const runtime = 'nodejs'
export async function GET() {
// 冷启动时间: ~200ms
const start = Date.now()
const fs = await import('fs/promises')
const data = await fs.readFile('data.json', 'utf8')
const duration = Date.now() - start
return NextResponse.json({
data: JSON.parse(data),
duration: `${duration}ms`,
})
}
2. 内存使用
// Edge Runtime - 更少的内存使用
// app/api/lightweight/route.js
export const runtime = 'edge'
export async function GET() {
// 内存使用: ~10MB
const data = {
timestamp: Date.now(),
random: Math.random(),
}
return NextResponse.json(data)
}
// Node.js Runtime - 更多的内存使用
// app/api/heavyweight/route.js
export const runtime = 'nodejs'
export async function GET() {
// 内存使用: ~50MB
const fs = await import('fs/promises')
const path = await import('path')
const crypto = await import('crypto')
const data = {
timestamp: Date.now(),
hash: crypto.createHash('sha256').update('data').digest('hex'),
}
return NextResponse.json(data)
}
最佳实践
1. 运行时选择指南
// 选择 Edge Runtime 的情况
export const runtime = 'edge'
// ✅ 简单的 API 调用
// ✅ 地理位置处理
// ✅ 重定向和头部操作
// ✅ 轻量级数据处理
// ✅ 需要快速响应的场景
// 选择 Node.js Runtime 的情况
export const runtime = 'nodejs'
// ✅ 文件系统操作
// ✅ 数据库操作
// ✅ 复杂的计算
// ✅ 使用 Node.js 特定的包
// ✅ 需要长时间运行的任务
2. 混合使用
// app/api/hybrid/route.js
export const runtime = 'edge'
export async function GET(request) {
// 在 Edge Runtime 中处理简单逻辑
const { searchParams } = new URL(request.url)
const query = searchParams.get('q')
if (!query) {
return NextResponse.json({ error: 'Query required' }, { status: 400 })
}
// 调用 Node.js API 进行复杂处理
const response = await fetch(`${process.env.NODE_API_URL}/process`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
})
const data = await response.json()
return NextResponse.json(data)
}
3. 错误处理
// app/api/edge-with-error-handling/route.js
export const runtime = 'edge'
export async function GET(request) {
try {
const { searchParams } = new URL(request.url)
const query = searchParams.get('q')
if (!query) {
return NextResponse.json(
{ error: 'Query parameter is required' },
{ status: 400 }
)
}
const response = await fetch(`https://api.example.com/search?q=${query}`)
if (!response.ok) {
return NextResponse.json(
{ error: 'External API error' },
{ status: response.status }
)
}
const data = await response.json()
return NextResponse.json(data)
} catch (error) {
console.error('Edge Runtime error:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
总结
运行时选择要点:
Edge Runtime:
- 更快的冷启动
- 更少的内存使用
- 只支持 Web APIs
- 适合简单、快速的操作
Node.js Runtime:
- 支持所有 Node.js API
- 更多的内存和计算资源
- 适合复杂的操作
- 支持文件系统和数据库
选择指南:
- 简单 API 调用 → Edge Runtime
- 文件系统操作 → Node.js Runtime
- 数据库操作 → Node.js Runtime
- 地理位置处理 → Edge Runtime
- 重定向和头部操作 → Edge Runtime
最佳实践:
- 根据功能需求选择运行时
- 考虑性能要求
- 处理错误情况
- 监控运行时性能