name: nextjs description: Next.js 特定模式,包括 App Router、React Server Components、Server Actions、流式渲染、缓存和 Vercel 部署。 allowed-tools: Read, Write, Edit, Bash, Glob, Grep
Next.js 技能
为使用 App Router、React Server Components、Server Actions 和现代部署模式构建全栈 Next.js 应用程序提供专家级协助。
能力
- 使用 App Router 生成 Next.js 项目结构
- 实现 React Server Components (RSC) 模式
- 为表单处理和变更创建 Server Actions
- 配置高级路由(并行路由、拦截路由、路由组)
- 使用 Suspense 边界设置流式渲染
- 通过重新验证实现缓存策略
- 配置 Vercel 部署和边缘函数
使用场景
在以下情况时调用此技能:
- 引导一个新的 Next.js 15+ 应用程序
- 实现服务器端渲染和数据获取
- 创建 API 路由和 Server Actions
- 配置高级路由模式
- 为核心 Web 指标进行优化
输入参数
| 参数 | 类型 | 是否必需 | 描述 |
|---|---|---|---|
| projectName | 字符串 | 是 | Next.js 项目名称 |
| router | 字符串 | 否 | app (默认) 或 pages |
| features | 数组 | 否 | 要搭建的功能列表 |
| database | 字符串 | 否 | prisma, drizzle, none |
| auth | 字符串 | 否 | nextauth, clerk, none |
| styling | 字符串 | 否 | tailwind, css-modules, styled-components |
功能配置示例
{
"projectName": "my-saas",
"features": [
"authentication",
"api-routes",
"server-actions",
"parallel-routes",
"middleware"
],
"database": "prisma",
"auth": "nextauth",
"styling": "tailwind"
}
输出结构
my-saas/
├── app/
│ ├── layout.tsx # 根布局
│ ├── page.tsx # 首页
│ ├── loading.tsx # 加载 UI
│ ├── error.tsx # 错误边界
│ ├── not-found.tsx # 404 页面
│ ├── globals.css # 全局样式
│ ├── (auth)/ # 路由组
│ │ ├── login/
│ │ │ └── page.tsx
│ │ └── register/
│ │ └── page.tsx
│ ├── dashboard/
│ │ ├── layout.tsx # 仪表板布局
│ │ ├── page.tsx
│ │ ├── @sidebar/ # 并行路由
│ │ │ └── default.tsx
│ │ └── settings/
│ │ └── page.tsx
│ └── api/
│ └── [...route]/
│ └── route.ts # API 路由
├── components/
│ ├── ui/ # 可复用的 UI 组件
│ └── features/ # 特定功能组件
├── lib/
│ ├── actions/ # Server Actions
│ │ └── user-actions.ts
│ ├── db/ # 数据库工具
│ │ └── prisma.ts
│ └── utils/ # 工具函数
├── middleware.ts # 边缘中间件
├── next.config.ts # Next.js 配置
├── tailwind.config.ts # Tailwind 配置
└── package.json
生成的代码模式
App Router 布局
// app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: {
default: 'My SaaS',
template: '%s | My SaaS',
},
description: '一个现代化的 SaaS 应用程序',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}
React Server Component
// app/dashboard/page.tsx
import { Suspense } from 'react';
import { getUser } from '@/lib/db/user';
import { DashboardStats } from '@/components/features/dashboard-stats';
import { DashboardSkeleton } from '@/components/ui/skeletons';
export default async function DashboardPage() {
const user = await getUser();
return (
<main className="container mx-auto py-8">
<h1 className="text-3xl font-bold">欢迎,{user.name}</h1>
<Suspense fallback={<DashboardSkeleton />}>
<DashboardStats userId={user.id} />
</Suspense>
</main>
);
}
Server Action
// lib/actions/user-actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
import { prisma } from '@/lib/db/prisma';
const updateProfileSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
});
export async function updateProfile(formData: FormData) {
const validatedFields = updateProfileSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
});
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
};
}
const { name, email } = validatedFields.data;
try {
await prisma.user.update({
where: { id: getCurrentUserId() },
data: { name, email },
});
} catch (error) {
return { error: '更新个人资料失败' };
}
revalidatePath('/dashboard/settings');
redirect('/dashboard');
}
使用 Server Action 的表单
// app/dashboard/settings/page.tsx
import { updateProfile } from '@/lib/actions/user-actions';
import { SubmitButton } from '@/components/ui/submit-button';
export default function SettingsPage() {
return (
<form action={updateProfile} className="space-y-4">
<div>
<label htmlFor="name">姓名</label>
<input
id="name"
name="name"
type="text"
required
className="w-full border rounded px-3 py-2"
/>
</div>
<div>
<label htmlFor="email">邮箱</label>
<input
id="email"
name="email"
type="email"
required
className="w-full border rounded px-3 py-2"
/>
</div>
<SubmitButton>更新个人资料</SubmitButton>
</form>
);
}
中间件
// 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-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
API 路由处理器
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/db/prisma';
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const page = parseInt(searchParams.get('page') ?? '1');
const limit = parseInt(searchParams.get('limit') ?? '10');
const users = await prisma.user.findMany({
skip: (page - 1) * limit,
take: limit,
select: {
id: true,
name: true,
email: true,
},
});
return NextResponse.json({ users, page, limit });
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await prisma.user.create({
data: body,
});
return NextResponse.json(user, { status: 201 });
}
缓存策略
带重新验证的静态生成
// 每小时重新验证一次
export const revalidate = 3600;
export default async function Page() {
const data = await fetchData();
return <div>{/* 内容 */}</div>;
}
按需重新验证
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const { path, tag, secret } = await request.json();
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ error: '无效密钥' }, { status: 401 });
}
if (tag) {
revalidateTag(tag);
} else if (path) {
revalidatePath(path);
}
return NextResponse.json({ revalidated: true });
}
依赖项
{
"dependencies": {
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"typescript": "^5.7.0"
}
}
工作流程
- 创建项目结构 - 设置 App Router 目录
- 配置布局 - 根布局和嵌套布局
- 实现页面 - 服务器组件和客户端组件
- 添加 Server Actions - 表单处理和变更
- 配置中间件 - 认证和安全
- 设置 API 路由 - REST 或 tRPC 端点
- 优化缓存 - 静态和动态策略
- 部署到 Vercel - 边缘和无服务器配置
应用的最佳实践
- 使用 React Server Components 的 App Router
- TypeScript 严格模式
- 用于变更的 Server Actions
- 适当的加载和错误状态
- 使用 next/image 进行图像优化
- 使用 next/font 进行字体优化
- 用于 SEO 的元数据 API
- 边缘兼容代码
参考资料
- Next.js 文档:https://nextjs.org/docs
- Next.js DevTools MCP:https://github.com/vercel/next-devtools-mcp
- claude-code-nextjs-skills:https://github.com/laguagu/claude-code-nextjs-skills
- Vercel 部署:https://vercel.com/docs
目标流程
- nextjs-全栈开发
- 服务器组件架构
- api-路由实现
- 认证设置
- 部署优化