DEV Community

WDSEGA
WDSEGA

Posted on

Next.js 15全栈开发实战:Server Components、Server Actions与AI集成完整指南

Next.js 15全栈开发实战:Server Components、Server Actions与AI集成完整指南

Next.js 15 带来了 React Server Components 的成熟实现、增强的 Server Actions 以及对 AI 应用开发的原生支持。本文从实战代码出发,带你掌握 Next.js 15 全栈开发的核心技术栈。

App Router 架构

Next.js 15 的 App Router 基于文件系统路由,采用嵌套布局和并行路由设计:

app/
├── layout.tsx          # 根布局
├── page.tsx            # 首页 (/)
├── blog/
│   ├── layout.tsx      # 博客布局
│   └── [slug]/
│       └── page.tsx    # /blog/:slug
├── api/ai/chat/
│   └── route.ts        # API 端点
└── middleware.ts       # 全局中间件
Enter fullscreen mode Exit fullscreen mode

嵌套布局在路由切换时不会重新挂载,状态得以保留:

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="zh-CN">
      <body>
        <nav>
          <Link href="/">首页</Link>
          <Link href="/blog">博客</Link>
        </nav>
        {children}
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

React Server Components 实战

Server Components 是 Next.js 15 的默认渲染模式,可以直接在组件中访问数据库:

// app/blog/page.tsx - 服务端组件,零客户端 JS
import { db } from '@/lib/database';

export default async function BlogPage() {
  const posts = await db.post.findMany({
    orderBy: { createdAt: 'desc' },
    take: 10,
  });

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

需要交互时使用 'use client' 标记客户端组件:

'use client';
import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      点击次数:{count}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

配合 Suspense 实现流式加载:

import { Suspense } from 'react';

export default function DashboardPage() {
  return (
    <div className="grid grid-cols-2 gap-4">
      <Suspense fallback={<StatsSkeleton />}>
        <UserStats />
      </Suspense>
      <Suspense fallback={<ActivitySkeleton />}>
        <RecentActivity />
      </Suspense>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Server Actions 表单处理

Server Actions 让你直接在组件中定义服务端函数,无需手动编写 API:

// app/actions/post.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';

const CreatePostSchema = z.object({
  title: z.string().min(1).max(200),
  content: z.string().min(10),
});

export async function createPost(formData: FormData) {
  const validated = CreatePostSchema.parse({
    title: formData.get('title'),
    content: formData.get('content'),
  });

  await db.post.create({ data: validated });
  revalidatePath('/blog');
  redirect('/blog');
}
Enter fullscreen mode Exit fullscreen mode

Next.js 15 的 useActionState 提供优雅的表单状态管理:

'use client';
import { useActionState } from 'react';
import { createPost } from '@/app/actions/post';

type State = {
  errors?: { title?: string[]; content?: string[] };
  message?: string;
  success?: boolean;
};

export function CreatePostForm() {
  const [state, formAction, isPending] = useActionState<State, FormData>(
    async (prevState, formData) => {
      try {
        await createPost(formData);
        return { success: true, message: '文章发布成功!' };
      } catch (error) {
        if (error instanceof z.ZodError) {
          return { errors: error.flatten().fieldErrors };
        }
        return { message: '发布失败,请重试' };
      }
    },
    {}
  );

  return (
    <form action={formAction}>
      <input name="title" placeholder="文章标题" />
      {state.errors?.title && (
        <p className="text-red-500">{state.errors.title[0]}</p>
      )}
      <textarea name="content" placeholder="文章内容" />
      <button type="submit" disabled={isPending}>
        {isPending ? '发布中...' : '发布文章'}
      </button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Vercel AI SDK 集成

安装依赖后即可快速搭建 AI 聊天功能:

npm install ai @ai-sdk/openai
Enter fullscreen mode Exit fullscreen mode
// app/api/ai/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o'),
    system: '你是一个专业的技术助手。请用中文回答。',
    messages,
  });

  return result.toDataStreamResponse();
}
Enter fullscreen mode Exit fullscreen mode

前端聊天界面只需几行代码:

'use client';
import { useChat } from 'ai/react';

export function ChatWindow() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } =
    useChat();

  return (
    <div className="flex flex-col h-screen max-w-2xl mx-auto">
      <div className="flex-1 overflow-y-auto p-4">
        {messages.map((message) => (
          <div key={message.id} className="mb-4">
            <span className={
              message.role === 'user'
                ? 'inline-block bg-blue-500 text-white rounded-lg px-4 py-2'
                : 'inline-block bg-gray-100 rounded-lg px-4 py-2'
            }>
              {message.content}
            </span>
          </div>
        ))}
      </div>
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <div className="flex gap-2">
          <input
            value={input}
            onChange={handleInputChange}
            placeholder="输入你的问题..."
            className="flex-1 border rounded-lg px-4 py-2"
          />
          <button type="submit" className="bg-blue-500 text-white px-6 py-2 rounded-lg">
            发送
          </button>
        </div>
      </form>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

AI SDK 还支持结构化输出,让 AI 返回符合 Schema 的数据:

import { generateObject } from 'ai';
import { z } from 'zod';

const CodeAnalysisSchema = z.object({
  summary: z.string(),
  complexity: z.enum(['low', 'medium', 'high']),
  suggestions: z.array(
    z.object({
      type: z.enum(['performance', 'security', 'readability']),
      description: z.string(),
    })
  ),
});

export async function POST(req: Request) {
  const { code } = await req.json();
  const { object } = await generateObject({
    model: openai('gpt-4o'),
    schema: CodeAnalysisSchema,
    prompt: `分析以下代码质量:\n\n${code}`,
  });
  return Response.json(object);
}
Enter fullscreen mode Exit fullscreen mode

中间件认证

基于 JWT 的认证中间件,保护路由安全:

// 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')?.value;
  const publicPaths = ['/login', '/register', '/api/auth'];

  if (publicPaths.some((p) => request.nextUrl.pathname.startsWith(p))) {
    return NextResponse.next();
  }

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  try {
    const payload = verifyToken(token);
    const headers = new Headers(request.headers);
    headers.set('x-user-id', payload.userId);
    headers.set('x-user-role', payload.role);
    return NextResponse.next({ request: { headers } });
  } catch {
    const response = NextResponse.redirect(new URL('/login', request.url));
    response.cookies.delete('auth-token');
    return response;
  }
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
Enter fullscreen mode Exit fullscreen mode

总结

Next.js 15 通过 Server Components、Server Actions 和 AI SDK 的组合,为全栈开发提供了完整方案:

  • Server Components - 数据获取推向服务端,减少客户端 JS 体积
  • Server Actions - 简化表单处理,useActionState 实现优雅状态管理
  • Vercel AI SDK - 流式响应 + 结构化输出,AI 集成变得简单
  • 中间件 - 灵活的请求拦截,适合认证、限流等场景

掌握这些技术,你就能构建出高性能、可维护的现代全栈应用。


📢 本文为精简版,完整版包含独家工具推荐和深度分析,请访问 WD Tech Blog 查看!

关注我的博客获取最新科技资讯、AI教程和效率工具推荐!

Top comments (0)