name: nextjs-15-specialist description: 处理Next.js 15功能、App Router、服务器组件、服务器操作或数据获取模式时使用。确保正确使用服务器与客户端组件以及现代Next.js模式。 allowed-tools: Read, Grep, Glob, WebFetch
Next.js 15 + App Router 专家
Quetrex开发的完整Next.js 15参考指南。
此技能提供关于所有Next.js 15 App Router模式的全面指导,确保代理首次即能正确实现现代Next.js。
关键规则(绝不违反)
这些规则是不可协商的。违反将导致构建失败。
1. 始终使用来自next/image的<Image> - 绝不使用<img>
// ✅ 始终这样做
import Image from 'next/image'
<Image src="/logo.png" alt="Logo" width={200} height={100} />
<Image src={user.avatar} alt={user.name} width={40} height={40} />
// ❌ 绝不这样做 - 构建将失败
<img src="/logo.png" alt="Logo" />
<img src={user.avatar} alt={user.name} />
原因: Next.js Image组件提供自动优化、懒加载,并防止布局偏移。ESLint配置为在<img>使用时使构建失败。
2. 服务器组件是默认的 - 仅在需要时添加’use client’
3. 绝不创建异步客户端组件
4. 始终为fetch()指定缓存策略
何时使用此技能
在以下情况时使用此技能:
- 创建新路由或页面
- 实现数据获取(服务器组件 vs 客户端组件)
- 服务器与客户端组件决策
- 服务器操作和表单处理
- 流式传输和Suspense
- 元数据和SEO
- 路由处理程序(API路由)
- 缓存策略
- 性能优化
完整文档
此技能包含涵盖每个Next.js 15模式的全面指南:
📁 App Router 完整指南
45+ 示例涵盖:
- 基于文件的路由(page.tsx, layout.tsx, route.ts)
- 动态路由([id], […slug], [[…slug]])
- 路由组((group))
- 私有文件夹(_folder)
- 路由处理程序(API路由)
- 布局和模板
- 加载UI(loading.tsx)
- 错误边界(error.tsx, global-error.tsx)
- 未找到页面(not-found.tsx)
- 并行路由(@folder)
- 拦截路由((.)folder)
- 路由段配置(dynamic, revalidate, runtime)
🔄 数据获取完整指南
35+ 示例涵盖:
- 服务器组件数据获取(async/await)
- 客户端组件数据获取(useEffect, React Query, SWR)
- 并行数据获取(Promise.all)
- 顺序数据获取(防止瀑布流)
- 流式数据(Suspense边界)
- 服务器发送事件(用于Quetrex语音的SSE)
- 数据变更(服务器操作)
- 乐观更新(useOptimistic)
- 表单处理(useFormStatus, useActionState)
- 请求去重
- 预加载数据
💾 缓存策略指南
35+ 示例涵盖:
- 请求记忆化(自动去重)
- 数据缓存(fetch缓存行为)
- 完整路由缓存(静态 vs 动态)
- 路由器缓存(客户端缓存)
- 缓存配置(force-cache, no-store, revalidate)
- 缓存标签(revalidateTag, revalidatePath)
- 按需重新验证
- 基于时间的重新验证(ISR)
- 缓存调试技术
- 选择退出缓存
⚡ 服务器操作完整指南
31+ 示例涵盖:
- 基本服务器操作模式
- 表单操作(渐进增强)
- 按钮操作(程序化调用)
- useFormStatus钩子(加载状态)
- useActionState钩子(状态管理)
- useOptimistic钩子(乐观UI)
- 操作中的错误处理
- 使用Zod验证
- 返回JSON vs 重定向
- 安全性(身份验证,CSRF)
- 速率限制
- 数据库事务
🎯 元数据API指南
26+ 示例涵盖:
- 静态元数据(导出对象)
- 动态元数据(generateMetadata)
- 基于文件的元数据(icon.png, opengraph-image.png)
- Open Graph元数据
- Twitter卡片
- JSON-LD结构化数据
- 视口配置
- PWA清单
- Robots.txt
- 站点地图生成
✅ 模式验证器
可执行的Python脚本,检查:
- 服务器组件不使用仅限客户端的API
- 客户端组件具有’use client’指令
- 数据获取使用适当的缓存策略
- 服务器操作标记为’use server’
- 元数据API使用正确
- 图像优化(使用<Image>而非<img>)
- 重量级组件的动态导入
运行方式:python validate-patterns.py /path/to/src
快速参考
服务器与客户端组件决策树
是否需要交互性(onClick, onChange等)?
├─ 是 → 客户端组件('use client')
└─ 否 → 服务器组件(默认)
是否需要React钩子(useState, useEffect)?
├─ 是 → 客户端组件
└─ 否 → 服务器组件
是否需要浏览器API(window, localStorage)?
├─ 是 → 客户端组件
└─ 否 → 服务器组件
是否需要获取数据?
├─ 使用服务器组件(首选)
└─ 仅在数据必须位于客户端时使用客户端组件
组件是否纯粹是展示性的?
└─ 服务器组件(性能更好)
常见模式
1. 服务器组件数据获取
// app/projects/page.tsx
export default async function ProjectsPage() {
const projects = await db.project.findMany()
return <ProjectList projects={projects} />
}
2. 具有交互性的客户端组件
// components/ProjectCard.tsx
'use client'
import { useState } from 'react'
export function ProjectCard({ project }: Props) {
const [loading, setLoading] = useState(false)
const handleDelete = async () => {
setLoading(true)
await deleteProject(project.id)
setLoading(false)
}
return (
<div>
<h2>{project.name}</h2>
<button onClick={handleDelete} disabled={loading}>
删除
</button>
</div>
)
}
3. 带表单的服务器操作
// app/actions.ts
'use server'
export async function createProject(formData: FormData) {
const name = formData.get('name') as string
const project = await db.project.create({ data: { name } })
revalidatePath('/projects')
return { success: true, project }
}
// app/projects/new/page.tsx
import { createProject } from '@/app/actions'
export default function NewProjectPage() {
return (
<form action={createProject}>
<input name="name" required />
<button type="submit">创建</button>
</form>
)
}
4. 使用Suspense进行流式传输
// app/dashboard/page.tsx
import { Suspense } from 'react'
export default function DashboardPage() {
return (
<div>
<Suspense fallback={<ProjectsSkeleton />}>
<ProjectsAsync />
</Suspense>
<Suspense fallback={<UsersSkeleton />}>
<UsersAsync />
</Suspense>
</div>
)
}
async function ProjectsAsync() {
const projects = await fetchProjects() // 慢查询
return <ProjectList projects={projects} />
}
5. 动态元数据
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next'
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>
}): Promise<Metadata> {
const { slug } = await params
const post = await fetchPost(slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.coverImage],
},
}
}
Quetrex最佳实践
1. 默认为服务器组件
// ✅ 做:服务器组件(默认)
export default async function ProjectsPage() {
const projects = await fetchProjects()
return <ProjectList projects={projects} />
}
// ❌ 不做:不需要时使用客户端组件
'use client'
export default function ProjectsPage() {
const [projects, setProjects] = useState([])
useEffect(() => {
fetchProjects().then(setProjects)
}, [])
return <ProjectList projects={projects} />
}
2. 使用适当的缓存策略
// 静态内容(永久缓存)
const categories = await fetch('https://api.example.com/categories', {
cache: 'force-cache',
}).then(r => r.json())
// 动态内容(无缓存)
const user = await fetch('https://api.example.com/me', {
cache: 'no-store',
}).then(r => r.json())
// ISR(每小时重新验证)
const products = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 },
}).then(r => r.json())
3. 实现适当的错误边界
// app/dashboard/error.tsx
'use client'
export default function DashboardError({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div>
<h2>出错了!</h2>
<button onClick={reset}>重试</button>
</div>
)
}
4. 使用加载状态
// app/dashboard/loading.tsx
export default function DashboardLoading() {
return <DashboardSkeleton />
}
5. 优化图像
// ✅ 做:使用next/image
import Image from 'next/image'
export function ProjectCard({ project }) {
return (
<Image
src={project.image}
alt={project.name}
width={400}
height={300}
/>
)
}
// ❌ 不做:使用<img>标签
export function ProjectCard({ project }) {
return <img src={project.image} alt={project.name} />
}
常见错误避免
❌ 错误1:异步客户端组件
// ❌ 不做:这是语法错误
'use client'
export default async function BadComponent() {
const data = await fetch('/api/data')
return <div>{data}</div>
}
// ✅ 做:使用服务器组件或useEffect
export default async function GoodComponent() {
const data = await fetch('/api/data')
return <div>{data}</div>
}
❌ 错误2:服务器组件中的客户端API
// ❌ 不做:服务器组件不能使用浏览器API
export default function BadComponent() {
const [state, setState] = useState(false) // 错误!
return <div>{state}</div>
}
// ✅ 做:添加'use client'指令
'use client'
export default function GoodComponent() {
const [state, setState] = useState(false)
return <div>{state}</div>
}
❌ 错误3:缺少缓存策略
// ❌ 不做:缓存行为不明确
const data = await fetch('/api/data')
// ✅ 做:明确的缓存策略
const data = await fetch('/api/data', {
cache: 'no-store', // 或 'force-cache', 或 { next: { revalidate: 60 } }
})
❌ 错误4:不使用<Image>
// ❌ 不做:未优化的图像
<img src="/logo.png" alt="Logo" />
// ✅ 做:使用Next.js图像优化
<Image src="/logo.png" alt="Logo" width={200} height={100} />
故障排除
错误:“您正在导入一个需要useState的组件…”
解决方案: 向组件文件添加'use client'。
错误:“async/await在客户端组件中无效”
解决方案: 移除'use client'或使用useEffect代替异步组件。
错误:“process未定义”
解决方案: 客户端组件中的环境变量需要NEXT_PUBLIC_前缀。
错误:“Headers已发送”
解决方案: 发送响应后不要使用headers()或cookies()。在任何流式传输之前调用它们。
官方文档
- Next.js 15.5 文档: https://nextjs.org/docs
- App Router: https://nextjs.org/docs/app
- 数据获取: https://nextjs.org/docs/app/building-your-application/data-fetching
- 服务器操作: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations
- 缓存: https://nextjs.org/docs/app/building-your-application/caching
- 元数据: https://nextjs.org/docs/app/building-your-application/optimizing/metadata
- 示例: https://github.com/vercel/next.js/tree/canary/examples
验证
运行模式验证器检查您的代码:
python .claude/skills/nextjs-15-specialist/validate-patterns.py src/
验证器检查:
- ✅ 异步客户端组件(禁止)
- ✅ 服务器组件中的客户端API
- ✅ 'use client’指令放置
- ✅ 服务器操作异步函数
- ✅ 图像优化
- ✅ 动态路由中的元数据
- ✅ 重量级组件的动态导入
- ✅ Fetch缓存策略
- ✅ 路由段配置
总结
此技能确保您:
- 选择正确的组件类型(服务器 vs 客户端)
- 实现适当的数据获取模式
- 使用适当的缓存策略
- 正确处理服务器操作
- 优化元数据和SEO
- 避免常见的Next.js错误
- 遵循Quetrex的架构指南
如有疑问:
- 阅读特定指南(上方链接)
- 运行验证器
- 检查官方Next.js 15文档
- 默认为服务器组件
最后更新:2025-11-23 Next.js版本:15.5 总示例:150+ 总行数:4,000+