nextjs-app-router
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNext.js App Router (Next.js 16+)
Next.js App Router(Next.js 16+)
Build modern React applications using Next.js 16+ with App Router architecture.
使用Next.js 16+的App Router架构构建现代化React应用。
Overview
概述
This skill provides patterns for:
- Server Components (default) and Client Components ("use client")
- Server Actions for mutations and form handling
- Route Handlers for API endpoints
- Explicit caching with "use cache" directive
- Parallel and intercepting routes
- Next.js 16 async APIs and proxy.ts
本技能提供以下场景的实现模式:
- Server Components(默认)与Client Components("use client")
- 用于数据变更和表单处理的Server Actions
- 用于API端点的Route Handlers
- 基于"use cache"指令的显式缓存
- 并行路由与拦截路由
- Next.js 16异步API与proxy.ts
When to Use
适用场景
Activate when user requests involve:
- "Create a Next.js 16 project", "Set up App Router"
- "Server Component", "Client Component", "use client"
- "Server Action", "form submission", "mutation"
- "Route Handler", "API endpoint", "route.ts"
- "use cache", "cacheLife", "cacheTag", "revalidation"
- "parallel routes", "@slot", "intercepting routes"
- "proxy.ts", "migrate from middleware.ts"
- "layout.tsx", "page.tsx", "loading.tsx", "error.tsx", "not-found.tsx"
- "generateMetadata", "next/image", "next/font"
当用户需求涉及以下内容时启用:
- "创建Next.js 16项目"、"设置App Router"
- "Server Component"、"Client Component"、"use client"
- "Server Action"、"表单提交"、"数据变更"
- "Route Handler"、"API端点"、"route.ts"
- "use cache"、"cacheLife"、"cacheTag"、"重新验证"
- "并行路由"、"@slot"、"拦截路由"
- "proxy.ts"、"从middleware.ts迁移"
- "layout.tsx"、"page.tsx"、"loading.tsx"、"error.tsx"、"not-found.tsx"
- "generateMetadata"、"next/image"、"next/font"
Quick Reference
快速参考
File Conventions
文件约定
| File | Purpose |
|---|---|
| Route page component |
| Shared layout wrapper |
| Suspense loading UI |
| Error boundary |
| 404 page |
| Re-mounting layout |
| API Route Handler |
| Parallel route fallback |
| Routing boundary (Next.js 16) |
| 文件 | 用途 |
|---|---|
| 路由页面组件 |
| 共享布局包装器 |
| Suspense加载UI |
| 错误边界 |
| 404页面 |
| 可重新挂载的布局 |
| API路由处理器 |
| 并行路由回退组件 |
| 路由边界(Next.js 16) |
Directives
指令
| Directive | Purpose |
|---|---|
| Mark Server Action functions |
| Mark Client Component boundary |
| Enable explicit caching (Next.js 16) |
| 指令 | 用途 |
|---|---|
| 标记Server Action函数 |
| 标记Client Component边界 |
| 启用显式缓存(Next.js 16+) |
Instructions
操作指南
Create New Project
创建新项目
bash
npx create-next-app@latest my-app --typescript --tailwind --app --turbopackbash
npx create-next-app@latest my-app --typescript --tailwind --app --turbopackImplement Server Component
实现Server Component
Server Components are the default in App Router.
tsx
// app/users/page.tsx
async function getUsers() {
const res = await fetch('https://api.example.com/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>
);
}在App Router中,Server Component是默认类型。
tsx
// app/users/page.tsx
async function getUsers() {
const res = await fetch('https://api.example.com/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>
);
}Implement Client Component
实现Client Component
Add when using hooks, browser APIs, or event handlers.
"use client"tsx
"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>
);
}当使用React Hooks、浏览器API或事件处理器时,添加指令。
"use client"tsx
"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>
);
}Create Server Action
创建Server Action
Define actions in separate files with "use server" directive.
tsx
// 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 with forms in Client Components:
tsx
"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>
);
}See references/server-actions.md for validation with Zod, optimistic updates, and advanced patterns.
在单独文件中使用"use server"指令定义动作。
tsx
// 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");
}在Client Component中结合表单使用:
tsx
"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>
);
}如需了解使用Zod验证、乐观更新及进阶模式,请参考references/server-actions.md。
Configure Caching
配置缓存
Use "use cache" directive for explicit caching (Next.js 16+).
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}`);
cacheLife("hours");
const product = await fetchProduct(id);
return <ProductDetail product={product} />;
}See references/caching-strategies.md for cache profiles, on-demand revalidation, and advanced caching patterns.
使用"use cache"指令实现显式缓存(Next.js 16+)。
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}`);
cacheLife("hours");
const product = await fetchProduct(id);
return <ProductDetail product={product} />;
}如需了解缓存配置文件、按需重新验证及进阶缓存模式,请参考references/caching-strategies.md。
Create Route Handler
创建Route Handler
Implement API endpoints using Route Handlers.
ts
// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const users = await db.user.findMany();
return NextResponse.json(users);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await db.user.create({ data: body });
return NextResponse.json(user, { status: 201 });
}Dynamic segments use :
[param]ts
// app/api/users/[id]/route.ts
interface RouteParams {
params: Promise<{ id: string }>;
}
export async function GET(request: NextRequest, { params }: RouteParams) {
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);
}使用Route Handlers实现API端点。
ts
// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const users = await db.user.findMany();
return NextResponse.json(users);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await db.user.create({ data: body });
return NextResponse.json(user, { status: 201 });
}动态路由段使用命名:
[param]ts
// app/api/users/[id]/route.ts
interface RouteParams {
params: Promise<{ id: string }>;
}
export async function GET(request: NextRequest, { params }: RouteParams) {
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);
}Handle Next.js 16 Async APIs
处理Next.js 16异步API
All Next.js APIs are async in version 16.
tsx
import { cookies, headers, draftMode } from "next/headers";
export default async function Page() {
const cookieStore = await cookies();
const headersList = await headers();
const { isEnabled } = await draftMode();
const session = cookieStore.get("session")?.value;
const userAgent = headersList.get("user-agent");
return <div>...</div>;
}Params and searchParams are also async:
tsx
export default async function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<{ sort?: string }>;
}) {
const { slug } = await params;
const { sort } = await searchParams;
// ...
}See references/nextjs16-migration.md for migration guide and proxy.ts configuration.
在Next.js 16中,所有框架API均为异步类型。
tsx
import { cookies, headers, draftMode } from "next/headers";
export default async function Page() {
const cookieStore = await cookies();
const headersList = await headers();
const { isEnabled } = await draftMode();
const session = cookieStore.get("session")?.value;
const userAgent = headersList.get("user-agent");
return <div>...</div>;
}Params和searchParams同样为异步类型:
tsx
export default async function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<{ sort?: string }>;
}) {
const { slug } = await params;
const { sort } = await searchParams;
// ...
}如需了解迁移指南及proxy.ts配置,请参考references/nextjs16-migration.md。
Implement Parallel Routes
实现并行路由
Use convention for parallel route slots.
@foldertsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
team,
analytics,
}: {
children: React.ReactNode;
team: React.ReactNode;
analytics: React.ReactNode;
}) {
return (
<div>
{children}
<div className="grid grid-cols-2">
{team}
{analytics}
</div>
</div>
);
}tsx
// 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>;
}See references/routing-patterns.md for intercepting routes, route groups, and dynamic routes.
使用约定定义并行路由插槽。
@foldertsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
team,
analytics,
}: {
children: React.ReactNode;
team: React.ReactNode;
analytics: React.ReactNode;
}) {
return (
<div>
{children}
<div className="grid grid-cols-2">
{team}
{analytics}
</div>
</div>
);
}tsx
// 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。
Best Practices
最佳实践
Server vs Client Decision
Server与Client组件选择
- Start with Server Component (default)
- Use Client Component only for:
- React hooks (useState, useEffect, useContext)
- Browser APIs (window, document, localStorage)
- Event handlers (onClick, onSubmit)
- Client-only libraries
- 优先使用Server Component(默认类型)
- 仅在以下场景使用Client Component:
- React Hooks(useState、useEffect、useContext)
- 浏览器API(window、document、localStorage)
- 事件处理器(onClick、onSubmit)
- 仅客户端可用的库
Data Fetching
数据获取
- Fetch in Server Components when possible
- Use React's for deduplication
cache() - Parallelize independent fetches
- Add Suspense boundaries with
loading.tsx
- 尽可能在Server Component中获取数据
- 使用React的实现请求去重
cache() - 并行执行独立的数据请求
- 通过添加Suspense边界
loading.tsx
Caching Strategy
缓存策略
tsx
"use cache";
import { cacheLife, cacheTag } from "next/cache";
// Set cache duration
cacheLife("hours");
// Tag for revalidation
cacheTag("resource-name");tsx
"use cache";
import { cacheLife, cacheTag } from "next/cache";
// 设置缓存时长
cacheLife("hours");
// 为重新验证添加标签
cacheTag("resource-name");Performance Checklist
性能检查清单
- Use for Suspense boundaries
loading.tsx - Use for optimized images
next/image - Use for font optimization
next/font - Enable React Compiler in
next.config.ts - Add for error handling
error.tsx - Add for 404 handling
not-found.tsx
- 使用实现Suspense边界
loading.tsx - 使用优化图片加载
next/image - 使用优化字体加载
next/font - 在中启用React Compiler
next.config.ts - 添加处理错误
error.tsx - 添加处理404场景
not-found.tsx
Examples
示例
Example 1: Create Blog Post with Server Action
示例1:使用Server Action创建博客文章
Input: Create a form to submit blog posts with validation
Output:
tsx
// 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 };
}tsx
// 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>
);
}输入: 创建带验证的博客文章提交表单
输出:
tsx
// 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 };
}tsx
// 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>
);
}Example 2: Product Page with Caching
示例2:带缓存的商品详情页
Input: Create a cached product page with revalidation
Output:
tsx
// 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>
<p>${product.price}</p>
</article>
);
}tsx
// 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 });
}输入: 创建支持重新验证的缓存商品页面
输出:
tsx
// 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>
<p>${product.price}</p>
</article>
);
}tsx
// 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 });
}Example 3: Dashboard with Parallel Routes
示例3:带并行路由的仪表盘
Input: Create a dashboard with sidebar and main content areas
Output:
tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
sidebar,
stats,
}: {
children: React.ReactNode;
sidebar: React.ReactNode;
stats: 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>
);
}tsx
// 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>
<div>Revenue: {stats.revenue}</div>
</>
);
}输入: 创建包含侧边栏和主内容区的仪表盘
输出:
tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
sidebar,
stats,
}: {
children: React.ReactNode;
sidebar: React.ReactNode;
stats: 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>
);
}tsx
// 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>
<div>Revenue: {stats.revenue}</div>
</>
);
}Constraints and Warnings
限制与注意事项
Constraints
限制
- Server Components cannot use browser APIs or React hooks
- Client Components cannot be async (no direct data fetching)
- ,
cookies(),headers()are async in Next.js 16draftMode() - and
paramsare Promise-based in Next.js 16searchParams - Server Actions must be defined with "use server" directive
- Server Components无法使用浏览器API或React Hooks
- Client Components不能是异步类型(无法直接获取数据)
- 在Next.js 16中,、
cookies()、headers()均为异步函数draftMode() - 在Next.js 16中,和
params基于Promise实现searchParams - Server Actions必须使用"use server"指令标记
Warnings
注意事项
- Attempting to use in a Client Component will cause a build error
await - Accessing or
windowin Server Components will throw an errordocument - Forgetting to await or
cookies()in Next.js 16 will result in a Promise instead of the actual valuesheaders() - Server Actions without proper validation can expose your database to unauthorized access
- 在Client Component中使用会导致构建错误
await - 在Server Components中访问或
window会抛出错误document - 在Next.js 16中,若未等待或
cookies(),将返回Promise而非实际值headers() - 未添加验证的Server Actions可能导致数据库被未授权访问
References
参考资料
Consult these files for detailed patterns:
- references/app-router-fundamentals.md - Server/Client Components, file conventions, navigation, next/image, next/font
- references/routing-patterns.md - Parallel routes, intercepting routes, route groups, dynamic routes
- references/caching-strategies.md - "use cache", cacheLife, cacheTag, revalidation
- references/server-actions.md - Server Actions, useActionState, validation, optimistic updates
- references/nextjs16-migration.md - Async APIs, proxy.ts, Turbopack, config
- references/data-fetching.md - Data patterns, Suspense, streaming
- references/metadata-api.md - generateMetadata, OpenGraph, sitemap
如需了解详细模式,请查阅以下文件:
- references/app-router-fundamentals.md - Server/Client组件、文件约定、导航、next/image、next/font
- 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、站点地图