关于 nextjs-code-review
面向 Next.js App Router 项目的代码审查技能,检查组件边界、数据获取、缓存、安全性和生产就绪情况。它按严重程度整理问题,并提供修复示例和优先级建议。
主要功能
- 01检查服务端与客户端组件边界
- 02审查数据获取和缓存策略
- 03检查 Server Actions 的验证与安全
- 04评估中间件、路由处理与元数据
- 05输出分级问题及代码示例
适用场景
- 01合并前审查页面、布局和路由段
- 02检查 Server Actions 的授权与输入验证
- 03评估缓存和按需重新验证配置
- 04迁移或新增功能后检查 App Router 代码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill nextjs-code-review安装后在 Claude Code 中输入/nextjs-code-review确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/app-router-patterns.mdperformance.mdserver-components.md详细介绍
Next.js 代码审查
概述
根据 Server Components、Client Components、Server Actions、缓存策略和生产就绪性标准评估 Next.js App Router 代码。按严重程度分类给出可执行的审查结果,并附上具体代码示例。将架构分析委派给 typescript-software-architect-review Agent。
适用场景
- 合并前审查 Next.js 页面、布局和路由段
- 验证 Server Component 与 Client Component 的边界
- 检查 Server Actions 的安全性和正确性
- 审查数据获取模式(fetch、缓存、重新验证)
- 评估缓存策略(静态生成、ISR、动态渲染)
- 评估中间件实现(身份验证、重定向、重写)
- 审查 API 路由处理程序是否正确处理请求和响应
- 验证 SEO 元数据配置
- 检查 loading、error 和 not-found 页面的实现
- 实现新的 Next.js 功能或从 Pages Router 迁移后
说明
-
确定范围:确定正在审查哪些 Next.js 路由段和组件。使用
glob查找page.tsx、layout.tsx、loading.tsx、error.tsx、route.ts和middleware.ts文件。 -
分析组件边界:验证 Server Component / Client Component 是否正确分离。检查
'use client'是否仅放在必要的位置,并尽可能深入组件树。确保 Server Component 不会导入仅供客户端使用的模块。 -
审查数据获取:验证 fetch 模式——检查
cache和revalidate选项是否正确,是否使用Promise.all并行获取数据,以及是否避免请求瀑布。确认服务端数据获取不会向客户端暴露敏感数据。 -
评估缓存策略:审查静态渲染与动态渲染的选择。检查静态生成是否使用
generateStaticParams,按需重新验证是否使用revalidatePath/revalidateTag,以及 API 路由的缓存头是否正确。 -
评估 Server Actions:审查表单操作是否正确进行客户端和服务端验证、错误处理、使用
useOptimistic进行乐观更新,以及安全性(确保操作不会在未经授权的情况下暴露敏感操作)。 -
检查中间件:审查中间件的请求匹配、身份验证/授权逻辑、响应修改和性能影响是否正确。确认它仅在必要的路由上运行。
-
审查元数据和 SEO:检查
generateMetadata函数、Open Graph 标签、结构化数据、robots.txt和sitemap.xml配置。确认内容可变的页面已正确实现动态元数据。 -
验证审查结果:完成前,通过检查实际代码上下文来验证每个问题。确认确实存在模式违规,确保建议的修复适用于该代码库,并移除误报。
-
生成审查报告:生成结构化报告,包含按严重程度分类的审查结果(严重、警告、建议)、正面观察,以及附有代码示例的优先级建议。
示例
示例 1:Server/Client Component 边界
// ❌ Bad: Entire page marked as client when only a button needs interactivity
'use client';
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await fetch(`/api/products/${params.id}`);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<button onClick={() => addToCart(product.id)}>Add to Cart</button>
</div>
);
}
// ✅ Good: Server Component with isolated Client Component
// app/products/[id]/page.tsx (Server Component)
import { AddToCartButton } from './add-to-cart-button';
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = await getProduct(id);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<AddToCartButton productId={product.id} />
</div>
);
}
// app/products/[id]/add-to-cart-button.tsx (Client Component)
'use client';
export function AddToCartButton({ productId }: { productId: string }) {
return <button onClick={() => addToCart(productId)}>Add to Cart</button>;
}
示例 2:数据获取模式
// ❌ Bad: Sequential data fetching creates waterfall
export default async function DashboardPage() {
const user = await getUser();
const orders = await getOrders(user.id);
const analytics = await getAnalytics(user.id);
return <Dashboard user={user} orders={orders} analytics={analytics} />;
}
// ✅ Good: Parallel data fetching with proper Suspense boundaries
export default async function DashboardPage() {
const user = await getUser();
const [orders, analytics] = await Promise.all([
getOrders(user.id),
getAnalytics(user.id),
]);
return <Dashboard user={user} orders={orders} analytics={analytics} />;
}
// ✅ Even better: Streaming with Suspense for independent sections
export default async function DashboardPage() {
const user = await getUser();
return (
<div>
<UserHeader user={user} />
<Suspense fallback={<OrdersSkeleton />}>
<OrdersSection userId={user.id} />
</Suspense>
<Suspense fallback={<AnalyticsSkeleton />}>
<AnalyticsSection userId={user.id} />
</Suspense>
</div>
);
}
示例 3:Server Actions 安全性
// ❌ Bad: Server Action without validation or authorization
'use server';
export async function deleteUser(id: string) {
await db.user.delete({ where: { id } });
}
// ✅ Good: Server Action with validation, authorization, and error handling
'use server';
import { z } from 'zod';
import { auth } from '@/lib/auth';
import { revalidatePath } from 'next/cache';
const deleteUserSchema = z.object({ id: z.string().uuid() });
export async function deleteUser(rawData: { id: string }) {
const session = await auth();
if (!session || session.user.role !== 'admin') {
throw new Error('Unauthorized');
}
const { id } = deleteUserSchema.parse(rawData);
await db.user.delete({ where: { id } });
revalidatePath('/admin/users');
}
示例 4:缓存和重新验证
// ❌ Bad: No cache control, fetches on every request
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return <PostList posts={posts} />;
}
// ✅ Good: Explicit caching with time-based revalidation
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600, tags: ['blog-posts'] },
}).then(r => r.json());
return <PostList posts={posts} />;
}
// Revalidation in Server Action
'use server';
export async function publishPost(data: FormData) {
await db.post.create({ data: parseFormData(data) });
revalidateTag('blog-posts');
}
示例 5:中间件审查
// ❌ Bad: Middleware runs on all routes including static assets
import { NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const session = request.cookies.get('session');
if (!session) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
// Missing config.matcher
// ✅ Good: Scoped middleware with proper matcher
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const session = request.cookies.get('session');
if (!session) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};
审查输出格式
按以下结构组织所有代码审查结果:
1. 摘要
简要概述整体质量评分(1-10)和关键观察结果。
2. 严重问题(必须修复)
导致安全漏洞、数据泄露或功能故障的问题。
3. 警告(应该修复)
违反最佳实践、造成性能问题或降低可维护性的问题。
4. 建议(可考虑改进)
针对代码组织、性能或开发者体验的改进。
5. 积极观察
值得认可的实现模式和良好实践。
6. 建议
按优先级列出后续步骤,并为影响最大的改进提供代码示例。
最佳实践
- 尽可能将
'use client'边界放在组件树更深处 - 在 Server Components 中获取数据——避免在客户端获取初始数据
- 并行获取数据(
Promise.all),避免请求瀑布 - 为每个路由段实现完善的加载、错误和未找到状态
- 使用 Zod 或类似库验证所有 Server Action 输入
- 尽可能使用
revalidatePath/revalidateTag,而不是基于时间的重新验证 - 使用
config.matcher将中间件限制在特定路由 - 为内容可变的动态页面实现
generateMetadata - 为参数已知的静态页面使用
generateStaticParams - 避免在 Client Components 中导入仅限服务器端的代码——使用
server-only包
限制和警告
- 此技能面向 Next.js App Router——Pages Router 模式可能有很大差异
- 遵循项目使用的 Next.js 版本——某些功能仅适用于特定版本
- 除非明确提出要求,否则不要建议从 Pages Router 迁移到 App Router
- 开发环境和生产环境中的缓存行为不同——请在生产构建中验证
- Server Actions 绝不能在缺少适当身份验证检查的情况下暴露敏感操作
- 聚焦于高置信度的问题——避免将风格偏好误判为问题
参考资料
详细的审查清单和模式文档请参阅 references/ 目录:
references/app-router-patterns.md— App Router 最佳实践和模式references/server-components.md— Server Component 与 Client Component 边界指南references/performance.md— Next.js 性能优化清单