正在加载,请稍候…

Next.js App Router:服务端组件、数据获取与缓存

掌握 Next.js 14 App Router,学习 React 服务端组件、Server Actions、数据获取模式、缓存策略及性能优化。

Next.js App Router: Server Components, Data Fetching, and Caching

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('');
  // ... 交互代码
}

Next.js App Router: Server Components, Data Fetching, and Caching illustration

数据获取模式

// 并行数据获取
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} />;
}

Next.js App Router: Server Components, Data Fetching, and Caching illustration

缓存

// 默认:无限期缓存(静态)
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'; // 始终动态

Next.js App Router: Server Components, Data Fetching, and Caching illustration

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 服务端组件代表了向以服务端为中心的前端开发的根本性转变。