Next.js App Router架构解析
Next.js App Router基于React Server Components(RSC)重新设计了渲染模型。页面组件默认在服务端执行,只有标记了"use client"的组件才在浏览器运行。这种模型让数据获取可以直接在组件内以同步风格编写,无需useEffect或getServerSideProps等API。
App Router的核心概念是布局(layout)和页面(page)的嵌套结构,layout不会在路由切换时重新渲染,page则是路由对应的渲染内容。理解Server Component和Client Component的边界划分,是正确使用App Router的前提。
Server Component数据获取模式
Server Component中直接使用async/await获取数据,框架自动处理请求去重和缓存:
// app/products/page.tsx - Server Component(默认)
export default async function ProductsPage() {
const products = await fetchProducts();
return (
<div>
<h1>商品列表</h1>
<ProductGrid products={products} />
</div>
);
}
// fetch默认缓存:force-cache
// 添加revalidate实现ISR(增量静态再生)
async function fetchProducts() {
const res = await fetch("https://api.example.com/products", {
next: { revalidate: 3600 }
});
return res.json();
}
三种渲染模式的选择策略
// 1. 静态生成(SSG):构建时生成,适合内容固定页面
export default function AboutPage() {
return <div>关于我们 - 静态内容</div>;
}
// 2. 服务端渲染(SSR):每次请求都重新渲染
async function getRealtimeData() {
const res = await fetch("https://api.example.com/realtime", {
cache: "no-store"
});
return res.json();
}
// 3. 增量静态再生(ISR):静态页面定时更新
async function getBlogPosts() {
const res = await fetch("https://api.example.com/posts", {
next: { revalidate: 600 }
});
return res.json();
}
选择依据:内容不频繁变选用SSG(营销页、文档);实时数据用SSR(仪表盘、交易页);更新有延迟容忍用ISR(博客、商品页)。
布局嵌套与状态保持
// app/layout.tsx - 根布局
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh">
<body>
<Navbar />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
// app/dashboard/layout.tsx - 仪表盘专属布局
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex">
<Sidebar />
<div className="flex-1">{children}</div>
</div>
);
}
Server与Client组件边界划分
// app/products/ProductFilter.tsx - Client Component
"use client";
import { useState } from "react";
export default function ProductFilter({ categories }: { categories: string[] }) {
const [selected, setSelected] = useState("all");
return (
<div className="flex gap-2">
{categories.map(cat => (
<button
key={cat}
onClick={() => setSelected(cat)}
className={selected === cat ? "active" : ""}
>
{cat}
</button>
))}
</div>
);
}
// app/products/page.tsx - Server Component导入Client组件
import ProductFilter from "./ProductFilter";
export default async function ProductsPage() {
const { products, categories } = await fetchData();
return (
<div>
<ProductFilter categories={categories} />
<ProductGrid products={products} />
</div>
);
}
边界原则:Server Component可以导入并渲染Client Component,Client Component不能导入Server Component。需要服务端逻辑时,通过props传递数据或使用Route Handler。
并行数据获取优化
// 并行获取(快)- 推荐
async function Page() {
const [user, orders] = await Promise.all([
fetchUser(),
fetchOrders()
]);
}
// 更优方案:使用Suspense流式渲染
import { Suspense } from "react";
async function Page() {
return (
<div>
<h1>用户中心</h1>
<Suspense fallback={<ProfileSkeleton />}>
<UserProfile />
</Suspense>
<Suspense fallback={<OrdersSkeleton />}>
<UserOrders />
</Suspense>
</div>
);
}
Route Handler替代API Routes
// app/api/webhook/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const body = await request.json();
const signature = request.headers.get("x-webhook-signature");
if (!verifySignature(body, signature)) {
return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
}
await processWebhook(body);
return NextResponse.json({ received: true });
}
// 流式响应(适合AI对话场景)
export async function GET() {
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
for await (const chunk of aiStream()) {
controller.enqueue(encoder.encode(chunk));
}
controller.close();
}
});
return new Response(stream, {
headers: { "Content-Type": "text/event-stream" }
});
}
App Router的渲染模型需要重新理解Server First的设计哲学,将交互边界收缩到最小范围,最大化服务端渲染比例,才能发挥RSC架构的性能优势。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/nextjsapprouter-fu-wu-duan-xuan-ran-yu-shu-ju-huo-qu-mo-shi/