关于 nextjs-app-router
用于使用 Next.js 16+ App Router 开发 React 应用的技能。内容涵盖服务端与客户端组件、Actions、路由处理器、缓存、路由模式及异步 API,适合创建或维护 Next.js 应用的开发者。
主要功能
- 01服务端与客户端组件模式
- 02Server Actions 和表单变更
- 03用于 API 接口的路由处理器
- 04显式缓存与重新验证
- 05并行路由和拦截路由
- 06Next.js 16 异步 API 指引
适用场景
- 01搭建 App Router 项目
- 02使用 Server Actions 构建表单
- 03通过路由处理器创建 API 接口
- 04添加缓存页面和并行仪表板路由
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill nextjs-app-router安装后在 Claude Code 中输入/nextjs-app-router确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/app-router-fundamentals.mdbest-practices.mdcaching-strategies.mddata-fetching.mdexamples.mdmetadata-api.mdnextjs16-migration.mdpatterns.mdrouting-patterns.mdserver-actions.md详细介绍
Next.js App Router(Next.js 16+)
使用 Next.js 16+ 和 App Router 架构构建现代 React 应用。
概览
此技能提供以下模式:服务器组件(默认)和客户端组件("use client")、用于变更和表单处理的服务器操作、用于 API 端点的路由处理器、使用 "use cache" 指令进行显式缓存、并行路由和拦截路由,以及 Next.js 16 的异步 API 和 proxy.ts。
适用场景
当用户请求涉及以下内容时启用:
- “创建 Next.js 16 项目”、“设置 App Router”
- “服务器组件”、“客户端组件”、“use client”
- “服务器操作”、“表单提交”、“变更”
- “路由处理器”、“API 端点”、“route.ts”
- “use cache”、“cacheLife”、“cacheTag”、“重新验证”
- “并行路由”、“
@插槽”、“拦截路由” - “proxy.ts”、“从 middleware.ts 迁移”
- “layout.tsx”、“page.tsx”、“loading.tsx”、“error.tsx”、“not-found.tsx”
- “generateMetadata”、“next/image”、“next/font”
快速参考
| 文件 | 用途 | 指令 | 用途 |
|---|---|---|---|
page.tsx | 路由页面 | "use server" | 服务器操作函数 |
layout.tsx | 共享布局 | "use client" | 客户端组件边界 |
loading.tsx | Suspense 加载状态 | "use cache" | 显式缓存(Next.js 16) |
error.tsx | 错误边界 | ||
not-found.tsx | 404 页面 | ||
route.ts | API 路由处理器 | ||
proxy.ts | 路由边界 |
说明
创建新项目
npx create-next-app@latest my-app --typescript --tailwind --app --turbopack
服务器组件
服务器组件是 App Router 中的默认组件。它们在服务器上运行,并可使用 async/await。
// app/users/page.tsx
async function getUsers() {
const apiUrl = process.env.API_URL;
const res = await fetch(`${apiUrl}/users`);
return res.json();
}
export default async function UsersPage() {
const users = await getUsers();
return <main>{users.map(user => <UserCard key={user.id} user={user} />)}</main>;
}
客户端组件
使用 hooks、浏览器 API 或事件处理程序时,添加 "use client"。
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}
服务器操作
在单独的文件中使用 "use server" 指令定义操作。
// app/actions.ts
"use server";
import { revalidatePath } from "next/cache";
export async function createUser(formData: FormData) {
const name = formData.get("name") as string;
const email = formData.get("email") as string;
await db.user.create({ data: { name, email } });
revalidatePath("/users");
}
在客户端组件中与表单搭配使用:
"use client";
import { useActionState } from "react";
import { createUser } from "./actions";
export default function UserForm() {
const [state, formAction, pending] = useActionState(createUser, {});
return (
<form action={formAction}>
<input name="name" />
<input name="email" type="email" />
<button type="submit" disabled={pending}>{pending ? "Creating..." : "Create"}</button>
</form>
);
}
参见 references/server-actions.md,了解 Zod 验证、乐观更新和高级模式。
配置缓存
使用 "use cache" 指令进行显式缓存(Next.js 16+)。
"use cache";
import { cacheLife, cacheTag } from "next/cache";
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
cacheTag(`product-${id}`);
cacheLife("hours");
const product = await fetchProduct(id);
return <ProductDetail product={product} />;
}
参见 references/caching-strategies.md,了解缓存配置、按需重新验证和高级模式。
路由处理器
// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
return NextResponse.json(await db.user.findMany());
}
export async function POST(request: NextRequest) {
const body = await request.json();
return NextResponse.json(await db.user.create({ data: body }), { status: 201 });
}
动态段使用 [param]:
// app/api/users/[id]/route.ts
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const user = await db.user.findUnique({ where: { id } });
if (!user) return NextResponse.json({ error: "Not found" }, { status: 404 });
return NextResponse.json(user);
}
Next.js 16 异步 API
在版本 16 中,所有 Next.js API 都是异步的。
import { cookies, headers } from "next/headers";
export default async function Page() {
const cookieStore = await cookies();
const headersList = await headers();
const session = cookieStore.get("session")?.value;
const userAgent = headersList.get("user-agent");
return <div>...</div>;
}
Params 和 searchParams 也基于 Promise:
export default async function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<{ sort?: string }>;
}) {
const { slug } = await params;
const { sort } = await searchParams;
// ...
}
参见 references/nextjs16-migration.md,了解迁移指南和 proxy.ts 配置。
并行路由
使用 @folder 约定创建并行路由插槽。
// app/dashboard/layout.tsx
export default function DashboardLayout({ children, team, analytics }: Record<string, React.ReactNode>) {
return (
<div>
{children}
<div className="grid grid-cols-2">{team}{analytics}</div>
</div>
);
}
// app/dashboard/@team/page.tsx
export default function TeamPage() { return <div>Team Section</div>; }
// app/dashboard/@analytics/page.tsx
export default function AnalyticsPage() { return <div>Analytics Section</div>; }
参见 references/routing-patterns.md,了解拦截路由、路由组和动态路由。
最佳实践
服务器端与客户端决策:
- 从服务器组件(默认)开始
- 仅在以下情况使用客户端组件:React hooks(useState、useEffect)、浏览器 API(window、document)、事件处理程序(onClick、onSubmit)或仅限客户端的库
数据获取:
- 尽可能在服务器组件中获取数据
- 使用 React 的
cache()去重 - 并行执行相互独立的数据获取
- 使用
loading.tsx添加 Suspense 边界
性能检查清单:
- 使用
loading.tsx设置 Suspense 边界 - 使用
next/image优化图片 - 使用
next/font优化字体 - 添加
error.tsx和not-found.tsx处理错误
示例
示例 1:使用服务器操作的博客文章表单
输入: 创建一个带有 Zod 验证的博客文章提交表单
输出:
// app/blog/actions.ts
"use server";
import { z } from "zod";
import { revalidatePath } from "next/cache";
const schema = z.object({ title: z.string().min(5), content: z.string().min(10) });
export async function createPost(formData: FormData) {
const parsed = schema.safeParse({ title: formData.get("title"), content: formData.get("content") });
if (!parsed.success) return { errors: parsed.error.flatten().fieldErrors };
await db.post.create({ data: parsed.data });
revalidatePath("/blog");
return { success: true };
}
// app/blog/new/page.tsx
"use client";
import { useActionState } from "react";
import { createPost } from "../actions";
export default function NewPostPage() {
const [state, formAction, pending] = useActionState(createPost, {});
return (
<form action={formAction}>
<input name="title" placeholder="Title" />
{state.errors?.title && <span>{state.errors.title[0]}</span>}
<textarea name="content" placeholder="Content" />
{state.errors?.content && <span>{state.errors.content[0]}</span>}
<button type="submit" disabled={pending}>{pending ? "Publishing..." : "Publish"}</button>
</form>
);
}
示例 2:带缓存的产品页面
输入: 创建一个支持按需重新验证的缓存产品页面
输出:
// app/products/[id]/page.tsx
"use cache";
import { cacheLife, cacheTag } from "next/cache";
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
cacheTag(`product-${id}`, "products");
cacheLife("hours");
const product = await db.product.findUnique({ where: { id } });
if (!product) notFound();
return <article><h1>{product.name}</h1><p>{product.description}</p></article>;
}
// app/api/revalidate/route.ts
import { revalidateTag } from "next/cache";
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const { tag } = await request.json();
revalidateTag(tag);
return NextResponse.json({ revalidated: true });
}
示例 3:带并行路由的仪表板
输入: 创建一个包含侧边栏和统计区域的仪表板
输出:
// app/dashboard/layout.tsx
export default function DashboardLayout({ children, sidebar, stats }: Record<string, React.ReactNode>) {
return (
<div className="flex">
<aside className="w-64">{sidebar}</aside>
<main className="flex-1"><div className="grid grid-cols-3">{stats}</div>{children}</main>
</div>
);
}
// app/dashboard/@sidebar/page.tsx
export default function Sidebar() { return <nav>{/* Navigation links */}</nav>; }
// app/dashboard/@stats/page.tsx
export default async function Stats() {
const stats = await fetchStats();
return <><div>Users: {stats.users}</div><div>Orders: {stats.orders}</div></>;
}
约束和警告
约束:
- 服务端组件不能使用浏览器 API 或 React Hooks
- 客户端组件不能是异步的(不能直接获取数据)
- 在 Next.js 16 中,
cookies()、headers()、draftMode()是异步的 - 在 Next.js 16 中,
params和searchParams基于 Promise - Server Actions 必须使用
"use server"指令定义
警告:
- 在客户端组件中使用
await会导致构建错误 - 在服务端组件中访问
window或document会抛出错误 - 在 Next.js 16 中,忘记对 cookies() 或 headers() 使用
await会返回 Promise,而非实际值 - 未进行适当验证的 Server Actions 可能会使数据库暴露给未授权访问
- 外部数据获取:使用
fetch()获取第三方 URL 的服务端组件会处理不可信内容——务必验证、清理并进行类型检查;使用环境变量配置 API URL,而不要硬编码
参考资料
- references/app-router-fundamentals.md — 服务端/客户端组件、文件约定、导航
- references/routing-patterns.md — 并行路由、拦截路由、路由组
- references/caching-strategies.md — "use cache"、cacheLife、cacheTag、重新验证
- references/server-actions.md — Server Actions、useActionState、验证、乐观更新
- references/nextjs16-migration.md — 异步 API、proxy.ts、Turbopack、配置
- references/data-fetching.md — 数据模式、Suspense、流式传输
- references/metadata-api.md — generateMetadata、OpenGraph、站点地图