名称: 应用路由器 描述: 当用户要求“创建Next.js路由”、“添加页面”、“设置布局”、“实现加载状态”、“添加错误边界”、“组织路由”、“创建动态路由”,或需要指导Next.js App Router文件约定和路由模式时,应使用此技能。 版本: 1.0.0
Next.js应用路由器模式
概述
App Router是基于文件系统的Next.js路由器,构建在React服务器组件之上。它使用app/目录结构,其中文件夹定义路由,特殊文件控制UI行为。
核心文件约定
路由文件
每个路由段由一个文件夹定义。文件夹内的特殊文件控制行为:
| 文件 | 用途 |
|---|---|
page.tsx |
路由的独特UI,使路由公开可访问 |
layout.tsx |
共享的UI包装器,在导航间保持状态 |
loading.tsx |
使用React Suspense的加载UI |
error.tsx |
路由段的错误边界 |
not-found.tsx |
404响应的UI |
template.tsx |
类似于布局,但在导航时重新渲染 |
default.tsx |
并行路由的回退 |
文件夹约定
| 模式 | 用途 | 示例 |
|---|---|---|
folder/ |
路由段 | app/blog/ → /blog |
[folder]/ |
动态段 | app/blog/[slug]/ → /blog/:slug |
[...folder]/ |
捕获所有段 | app/docs/[...slug]/ → /docs/* |
[[...folder]]/ |
可选捕获所有 | app/shop/[[...slug]]/ → /shop或/shop/* |
(folder)/ |
路由组(无URL影响) | app/(marketing)/about/ → /about |
@folder/ |
命名插槽(并行路由) | app/@modal/login/ |
_folder/ |
私有文件夹(排除) | app/_components/ |
创建路由
基本路由结构
要创建新路由,添加一个带有page.tsx的文件夹:
app/
├── page.tsx # / (主页)
├── about/
│ └── page.tsx # /about
└── blog/
├── page.tsx # /blog
└── [slug]/
└── page.tsx # /blog/:slug
页面组件
页面默认是服务器组件:
// app/about/page.tsx
export default function AboutPage() {
return (
<main>
<h1>关于我们</h1>
<p>欢迎来到我们公司。</p>
</main>
)
}
动态路由
通过params属性访问路由参数:
// app/blog/[slug]/page.tsx
interface PageProps {
params: Promise<{ slug: string }>
}
export default async function BlogPost({ params }: PageProps) {
const { slug } = await params
const post = await getPost(slug)
return <article>{post.content}</article>
}
布局
根布局(必需)
每个应用都需要一个带有<html>和<body>的根布局:
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
嵌套布局
布局包装其子元素并保持状态:
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
)
}
加载和错误状态
加载UI
使用Suspense创建即时加载状态:
// app/dashboard/loading.tsx
export default function Loading() {
return <div className="animate-pulse">加载中...</div>
}
错误边界
优雅地处理错误:
// app/dashboard/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div>
<h2>出了点问题!</h2>
<button onClick={reset}>重试</button>
</div>
)
}
路由组
组织路由而不影响URL结构:
app/
├── (marketing)/
│ ├── layout.tsx # 营销布局
│ ├── about/page.tsx # /about
│ └── contact/page.tsx # /contact
└── (shop)/
├── layout.tsx # 商店布局
└── products/page.tsx # /products
元数据
静态元数据
// app/about/page.tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: '关于我们',
description: '了解更多关于我们公司的信息',
}
动态元数据
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
return { title: post.title }
}
关键模式
- 共位: 将组件、测试和样式保持在路由附近
- 私有文件夹: 使用
_folder用于非路由文件 - 路由组: 使用
(folder)组织而不影响URL - 并行路由: 使用
@slot进行复杂布局 - 拦截路由: 使用
(.)模式进行模态框
资源
详细模式,参见:
references/routing-conventions.md- 完整的文件约定references/layouts-templates.md- 布局组合模式references/loading-error-states.md- Suspense和错误处理examples/dynamic-routes.md- 动态路由示例examples/parallel-routes.md- 并行和拦截路由