
Next.js App Router:服务端组件、数据获取与缓存
服务端组件 vs 客户端组件
// app/users/page.tsx - 服务端组件(默认)
// 在服务端运行,不包含客户端 JavaScript
async function UsersPage() {
// 直接访问数据库!
const users = await db.query('SELECT * FROM users');
return (
<div>
<h1>用户</h1>
{users.map(user => <UserCard key={user.id} user={user} />)}
</div>
);
}
// app/users/SearchBar.tsx - 客户端组件
'use client'; // 需要交互性
import { useState } from 'react';
export function SearchBar({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState('');
// ... 交互代码
}

数据获取模式
// 并行数据获取
async function DashboardPage() {
// 两个请求同时开始
const [user, orders] = await Promise.all([
fetchUser(),
fetchOrders(),
]);
return <Dashboard user={user} orders={orders} />;
}
// 顺序获取(数据依赖前一个结果)
async function UserOrdersPage({ params }: { params: { userId: string } }) {
const user = await fetchUser(params.userId);
const orders = await fetchOrders(user.id); // 需要先获取 user.id
return <OrderList user={user} orders={orders} />;
}

缓存
// 默认:无限期缓存(静态)
const data = await fetch('https://api.example.com/products');
// 每 60 秒重新验证
const data = await fetch('https://api.example.com/products', {
next: { revalidate: 60 }
});
// 无缓存(动态)
const data = await fetch('https://api.example.com/user', {
cache: 'no-store'
});
// 路由段配置
// app/dashboard/layout.tsx
export const revalidate = 300; // 每 5 分钟重新验证
export const dynamic = 'force-dynamic'; // 始终动态

Server Actions
// app/users/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
export async function createUser(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
// 服务端验证
if (!email.includes('@')) {
return { error: '无效的电子邮件' };
}
await db.insert('users', { name, email });
revalidatePath('/users'); // 使缓存失效
redirect('/users'); // 成功后重定向
}
// app/users/new/page.tsx
import { createUser } from '../actions';
export default function NewUserPage() {
return (
<form action={createUser}>
<input name="name" placeholder="姓名" required />
<input name="email" type="email" placeholder="电子邮件" required />
<button type="submit">创建用户</button>
</form>
);
}
使用 Suspense 进行流式传输
import { Suspense } from 'react';
async function SlowComponent() {
await new Promise(r => setTimeout(r, 2000)); // 模拟慢速数据获取
const data = await fetchExpensiveData();
return <DataDisplay data={data} />;
}
export default function Page() {
return (
<div>
<h1>仪表盘</h1>
{/* 立即显示 */}
<QuickStats />
{/* 就绪时流式传输 - 加载时显示骨架屏 */}
<Suspense fallback={<Skeleton />}>
<SlowComponent />
</Suspense>
</div>
);
}
中间件
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 身份验证检查
const token = request.cookies.get('auth-token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// 添加头部
const response = NextResponse.next();
response.headers.set('x-pathname', request.nextUrl.pathname);
return response;
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
Next.js App Router 结合 React 服务端组件代表了向以服务端为中心的前端开发的根本性转变。