关于 nextjs-server-navigation
面向 Next.js 开发者的导航模式技能,说明如何在 Server Component 中使用 Link、redirect() 和 Server Action,并避免为简单导航引入客户端逻辑。
主要功能
- 01使用 Link 创建页面链接
- 02使用 redirect() 条件跳转
- 03通过 Server Action 处理表单导航
- 04区分服务端与客户端导航方式
适用场景
- 01为服务端页面添加静态链接
- 02按认证或权限结果跳转
- 03实现表单提交后的页面跳转
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ wsimmonds/ claude-nextjs-skills --skill nextjs-server-navigation安装后在 Claude Code 中输入/nextjs-server-navigation确认已加载。
详细介绍
Next.js:服务端组件导航模式
⚠️ 关键规则
服务端组件使用的导航方法与客户端组件不同!
当需求要求使用服务端渲染导航时,例如链接到其他页面、检查后重定向或演示路由模式,请优先在服务端组件中使用 <Link> 和 redirect()。除非涉及仅限客户端的 API,否则仍应避免使用 'use client'。
模式
场景: 构建一个演示正确导航模式的服务端组件
✅ 正确方案:
// app/page.tsx (Server Component - NO 'use client'!)
import Link from 'next/link';
export default async function Page() {
return (
<div>
<h1>Home</h1>
<Link href="/dashboard">Go to Dashboard</Link>
<Link href="/profile">View Profile</Link>
</div>
);
}
❌ 错误方案:
// app/page.tsx
'use client'; // ❌ NO! Server components don't need this for navigation!
import { useRouter } from 'next/navigation'; // ❌ Wrong for server components
export default function Page() {
const router = useRouter(); // ❌ This is client-side navigation
// ...
}
服务端导航方法
方法 1:Link 组件(推荐用于链接)
// app/page.tsx
import Link from 'next/link';
export default async function Page() {
// Can still fetch data - this is a server component!
const data = await fetchData();
return (
<div>
<h1>Welcome</h1>
{/* Simple navigation link */}
<Link href="/about">About Us</Link>
{/* Dynamic link */}
<Link href={`/products/${data.productId}`}>View Product</Link>
{/* Link with styling */}
<Link href="/dashboard" className="btn-primary">
Dashboard
</Link>
</div>
);
}
要点:
- ✅ 可在服务端组件中使用(无需
'use client') - ✅ 可以是异步函数
- ✅ 可以获取数据
- ✅ 无需使用 Hook
方法 2:redirect() 函数(用于条件重定向)
// app/profile/page.tsx
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';
export default async function ProfilePage() {
// Check authentication
const cookieStore = await cookies();
const session = cookieStore.get('session');
// Redirect if not authenticated
if (!session) {
redirect('/login');
}
// Fetch user data
const user = await fetchUser(session.value);
return <div>Welcome, {user.name}!</div>;
}
适用 redirect() 的情况:
- 根据服务端数据进行条件重定向
- 身份验证检查
- 权限验证
- 基于数据的路由
方法 3:使用服务端操作的按钮
// app/page.tsx
import { logout } from './actions';
export default async function Page() {
return (
<div>
<h1>Dashboard</h1>
<form action={logout}>
<button type="submit">Logout</button>
</form>
</div>
);
}
// app/actions.ts
'use server';
import { redirect } from 'next/navigation';
export async function logout() {
// Clear session
await clearSession();
// Redirect to login page
redirect('/login');
}
完整示例:导航模式
// app/page.tsx - Demonstrates multiple navigation patterns
import Link from 'next/link';
import { redirect } from 'next/navigation';
import { headers } from 'next/headers';
export default async function HomePage() {
// Server-side logic
const headersList = await headers();
const userAgent = headersList.get('user-agent');
// Conditional redirect example
if (userAgent?.includes('bot')) {
redirect('/bot-page');
}
return (
<div>
<h1>Welcome to Our App</h1>
{/* Navigation Links */}
<nav>
<Link href="/about">About</Link>
<Link href="/products">Products</Link>
<Link href="/contact">Contact</Link>
</nav>
{/* Button-style link */}
<Link href="/get-started" className="button">
Get Started
</Link>
{/* Dynamic link */}
<Link href={`/user/${123}`}>View Profile</Link>
</div>
);
}
TypeScript:绝不使用 any 类型
// ❌ WRONG
function handleClick(e: any) { ... }
// ✅ CORRECT - Not needed in server components!
// Server components don't have onClick handlers
// For client components with handlers:
'use client';
function handleClick(e: React.MouseEvent<HTMLButtonElement>) { ... }
服务端与客户端导航对比
| 特性 | 服务端组件 | 客户端组件 |
|---|---|---|
<Link> | ✅ 是 | ✅ 是 |
redirect() | ✅ 是 | ❌ 否 |
useRouter() | ❌ 否 | ✅ 是 |
usePathname() | ❌ 否 | ✅ 是 |
| async function | ✅ 是 | ❌ 否 |
| 'use client' | ❌ 否 | ✅ 是 |
应避免的常见错误
❌ 错误 1:为导航添加 'use client'
// ❌ WRONG
'use client'; // Don't add this just for navigation!
import Link from 'next/link';
export default function Page() {
return <Link href="/about">About</Link>;
}
// ✅ CORRECT
import Link from 'next/link';
// No 'use client' needed!
export default async function Page() {
return <Link href="/about">About</Link>;
}
❌ 错误 2:在服务端组件中使用 useRouter()
// ❌ WRONG
import { useRouter } from 'next/navigation'; // This is for CLIENT components!
export default async function Page() {
const router = useRouter(); // ERROR! Can't use hooks in server components
// ...
}
// ✅ CORRECT - Use Link or redirect()
import Link from 'next/link';
import { redirect } from 'next/navigation';
export default async function Page() {
// Conditional redirect
const shouldRedirect = await checkSomething();
if (shouldRedirect) {
redirect('/other-page');
}
// Or navigation links
return <Link href="/other-page">Go</Link>;
}
❌ 错误 3:仅为简单导航就将组件改为客户端组件
// ❌ WRONG - Loses server component benefits!
'use client';
export default function Page() {
return (
<div>
<Link href="/dashboard">Dashboard</Link>
</div>
);
}
// ✅ CORRECT - Keep it as a server component!
export default async function Page() {
// Can now fetch data server-side
const data = await fetchData();
return (
<div>
<Link href="/dashboard">Dashboard</Link>
<p>{data.message}</p>
</div>
);
}
高级模式
在服务端操作中进行编程式导航
// app/page.tsx
import { createPost } from './actions';
export default async function Page() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Create Post</button>
</form>
);
}
// app/actions.ts
'use server';
import { redirect } from 'next/navigation';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
// Save to database
const post = await db.posts.create({ title });
// Redirect to the new post
redirect(`/posts/${post.id}`);
}
服务端组件中的多个链接
// app/page.tsx
import Link from 'next/link';
export default async function NavigationPage() {
const pages = await fetchPages();
return (
<nav>
<h2>Site Navigation</h2>
<ul>
{pages.map((page) => (
<li key={page.id}>
<Link href={`/pages/${page.slug}`}>
{page.title}
</Link>
</li>
))}
</ul>
</nav>
);
}
快速决策树
Need navigation in a component?
│
├─ Is it a Server Component (no 'use client')?
│ ├─ Static link → Use <Link>
│ ├─ Conditional redirect → Use redirect()
│ └─ Form submission → Server Action with redirect()
│
└─ Is it a Client Component ('use client')?
├─ Link → Use <Link> (works in both!)
└─ Programmatic → Use useRouter()
何时改用客户端导航
仅当你需要以下功能时,才使用客户端组件('use client' + useRouter()):
- 根据客户端状态进行编程式导航
- 在客户端动画结束后导航
- 仅限浏览器的 API(window、localStorage)
- React Hook(useState、useEffect)
其他所有情况都使用服务端组件导航!
快速检查清单
看到“演示导航模式”时:
- 创建服务端组件(不使用 'use client')
- 从 'next/link' 导入
Link - 添加带有 href 属性的
<Link>组件 - 如果要获取数据,请将组件保留为
async - 不要从 next/navigation 导入 useRouter
- 不要添加 'use client' 指令
- 使用正确的 TypeScript 类型(不使用
any)
总结
服务端组件导航:
- ✅ 使用
<Link>创建导航链接 - ✅ 使用
redirect()进行条件重定向 - ✅ 如有需要,将组件保留为异步函数
- ✅ 无需使用 'use client'
- ✅ 无需使用 Hook
对于静态链接,这种模式比客户端导航更简单、性能更好!
评论
支持 Markdown