深色模式
Edge 渲染已经从概念验证走向生产应用。来总结一下我们团队在 Edge 渲染方面的实践和踩坑经验。
Edge 渲染模式对比
模式 首屏速度 动态内容 复杂度 成本
───────────────────────────────────────────────────
纯静态 SSG 最快 不支持 低 最低
Edge SSR 快 支持 中 中
Edge Streaming 快 支持 中高 中
Server Components 快 支持 高 中
混合模式 快 支持 高 灵活Edge SSR 实战
ts
// middleware.ts — Next.js Edge Middleware
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// 运行在 Edge Runtime(V8 isolate,不是 Node.js)
export function middleware(request: NextRequest) {
const country = request.geo?.country ?? "CN";
const language = request.headers.get("accept-language") ?? "zh-CN";
// Edge 层做 A/B 测试分流
const abGroup = request.cookies.get("ab-group")?.value ?? assignGroup(request);
// Edge 层做个性化重定向
if (country === "JP" && !request.nextUrl.pathname.startsWith("/ja")) {
return NextResponse.redirect(new URL(`/ja${request.nextUrl.pathname}`, request.url));
}
// 注入请求头,传递给 Server Components
const response = NextResponse.next();
response.headers.set("x-ab-group", abGroup);
response.headers.set("x-user-country", country);
return response;
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};Edge Streaming
tsx
// app/products/page.tsx — 流式渲染
import { Suspense } from "react";
// 快速部分:立即返回
export default function ProductsPage() {
return (
<div>
<h1>产品列表</h1>
<ProductFilters />
{/* 慢查询用 Suspense 包裹,流式返回 */}
<Suspense fallback={<ProductListSkeleton />}>
<ProductList />
</Suspense>
{/* 推荐数据也是异步的 */}
<Suspense fallback={<RecommendationsSkeleton />}>
<Recommendations />
</Suspense>
</div>
);
}
// 产品列表:在 Edge 上执行数据库查询
async function ProductList() {
// 这个函数在 Edge Runtime 上执行
const products = await db.product.findMany({
take: 20,
orderBy: { createdAt: "desc" },
});
return (
<div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
{products.map((p) => (
<ProductCard key={p.id} product={p} />
))}
</div>
);
}
// 推荐数据:调用远程 API
async function Recommendations() {
const recommendations = await fetch("https://api.example.com/recommendations", {
next: { revalidate: 3600 }, // 1 小时缓存
}).then((r) => r.json());
return (
<section>
<h2>为你推荐</h2>
<RecommendationList items={recommendations} />
</section>
);
}Edge 数据缓存策略
ts
// lib/edge-cache.ts
// Edge 上的多级缓存
interface CacheConfig {
staleWhileRevalidate: number; // 后台刷新时间
maxAge: number; // 缓存有效期
tags: string[]; // 缓存标签(用于按标签失效)
}
// 方案 1:Next.js fetch 缓存
const products = await fetch("https://api.example.com/products", {
next: {
revalidate: 60, // 60 秒后重新验证
tags: ["products"], // 可通过 revalidateTag("products") 失效
},
});
// 方案 2:KV 缓存(Cloudflare Workers KV / Vercel KV)
import { kv } from "@vercel/kv";
async function getCachedProducts() {
const cacheKey = "products:all";
// 先查缓存
const cached = await kv.get(cacheKey);
if (cached) return cached;
// 缓存未命中,查数据库
const products = await db.product.findMany();
await kv.set(cacheKey, products, { ex: 60 });
return products;
}
// 方案 3:Edge Config(只读配置,毫秒级读取)
import { get } from "@vercel/edge-config";
async function getFeatureFlags() {
// Edge Config 的读取延迟 < 1ms
const flags = await get("feature-flags");
return flags;
}Edge 的限制和坑
Edge Runtime 限制:
1. 不支持 Node.js API(fs、crypto 需要 Web Crypto 替代)
2. 内存限制(通常 128MB)
3. 执行时间限制(通常 30 秒)
4. 不支持某些 npm 包(依赖 Node.js 内置模块的)
踩坑记录:
1. Prisma 在 Edge 上需要特殊配置(driver adapters)
2. 某些日期库(如 moment)不兼容,改用 date-fns 或 Intl
3. 图片处理需要调用外部服务,不能用 sharp
4. 调试困难,日志不完整ts
// Edge 兼容的 Prisma 配置
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
// lib/db.ts
import { PrismaNeon } from "@prisma/adapter-neon";
import { PrismaClient } from "@prisma/client";
const adapter = new PrismaNeon({ connectionString: process.env.DATABASE_URL! });
const prisma = new PrismaClient({ adapter });混合渲染策略
我们的渲染策略:
/(首页) → Edge SSR + Streaming
/products(列表) → Edge SSR + 缓存
/products/[id](详情)→ Edge SSR + ISR(1 小时)
/dashboard(后台) → Client-side(纯 SPA)
/blog(博客) → SSG(构建时生成)
/api/*(接口) → Edge Functions
根据页面特性选择渲染模式,不是所有页面都需要 Edge SSR。小结
- Edge 渲染适合需要低延迟首屏的公开页面
- Streaming SSR 是 Edge 渲染的核心模式,让快的部分先到
- 缓存策略是 Edge 渲染的关键,多级缓存降低源站压力
- Edge Runtime 有 Node.js API 限制,选库时要注意兼容性
- 混合渲染模式是生产环境的最优解,按页面特性选择