Next.js 路由使用指南
目录
- 基础路由
- index 路由
- 页面路由
- 布局路由
- 嵌套路由
- 文件夹嵌套
- 共享布局
- 动态路由
- 单参数路由
- 多参数路由
- 可选参数
- 路由组
- 组织结构
- 共享布局
- 平行路由
- 同时渲染
- 条件渲染
- 拦截路由
- 模态框
- 照片预览
- 最佳实践
- 路由组织
- 性能优化
- 类型安全
1. 基础路由
Next.js 13+ 使用基于文件系统的路由方式,在 app 目录下创建的文件夹会自动映射为路由路径。
1.1 index 路由
typescript
// app/page.tsx - 首页路由 "/"
export default function Home() {return (<main><h1>欢迎来到首页</h1><p>这是网站的主页内容</p></main>);
}
// app/about/page.tsx - 关于页面路由 "/about"
export default function About() {return (<div><h1>关于我们</h1><p>这是关于页面的内容</p></div>);
}
1.2 布局路由
/ app/layout.tsx - 根布局
export default function RootLayout({children,}: {children: React.ReactNode;}) {return (<html lang="zh"><body><header><nav><a href="/">首页</a><a href="/about">关于</a><a href="/blog">博客</a></nav></header><main>{children}</main><footer><p>© 2024 我的网站</p></footer></body></html>);
}
2. 嵌套路由
2.1 基本嵌套
typescript
// app/blog/layout.tsx - 博客布局
export default function BlogLayout({children,}: {children: React.ReactNode;
}) {return (<div className="blog-container"><aside><h3>博客分类</h3><nav><a href="/blog/tech">技术</a><a href="/blog/life">生活</a></nav></aside><section>{children}</section></div>);
}
// app/blog/page.tsx - 博客首页
export default function BlogIndex() {return <h1>博客文章列表</h1>;
}
// app/blog/tech/page.tsx - 技术博客
export default function TechBlog() {return <h1>技术文章</h1>;
}
2.2 共享布局
typescript
// app/(marketing)/layout.tsx - 营销页面共享布局
export default function MarketingLayout({children,}: {children: React.ReactNode;}) {return (<div className="marketing-layout"><nav className="marketing-nav"><a href="/products">产品</a><a href="/pricing">价格</a><a href="/contact">联系我们</a></nav>{children}</div>);
}
3. 动态路由
3.1 单参数动态路由
typescript
// app/posts/[id]/page.tsx
interface Props {
params: { id: string }
}
export default function Post({ params }: Props) {return (<article><h1>文章 {params.id}</h1></article>);
}
// 生成静态路由参数
export async function generateStaticParams() {const posts = await fetch('https://api.example.com/posts').then(res => res.json());return posts.map((post) => ({id: post.id.toString(),}));
}
3.2 多参数动态路由
typescript
// app/shop/[category]/[product]/page.tsx
interface Props {params: {category: string;product: string;}
}
export default function Product({ params }: Props) {return (<div><h1>{params.product}</h1><p>分类: {params.category}</p></div>);
}
3.3 可选参数路由
typescript
// app/blog/[[...slug]]/page.tsx
interface Props {params: { slug: string[] | undefined }
}
export default function BlogPost({ params }: Props) {if (!params.slug) {return <h1>博客首页</h1>;}return (<article><h1>博客路径: {params.slug.join('/')}</h1></article>);
}
4. 路由组
4.1 基本路由组
typescript
// app/(shop)/layout.tsx
export default function ShopLayout({children,}: {
children: React.ReactNode;}) {return (<div className="shop-container"><nav className="shop-nav"><a href="/products">所有产品</a><a href="/cart">购物车</a></nav>{children}</div>);
}
// app/(shop)/products/page.tsx
export default function Products() {return <h1>产品列表</h1>;
}
// app/(shop)/cart/page.tsx
export default function Cart() {return <h1>购物车</h1>;
}
5. 平行路由
5.1 基本平行路由
typescript
// app/layout.tsx
export default function Layout(props: {
children: React.ReactNode;
modal: React.ReactNode;
sidebar: React.ReactNode;
}) {return (<html><body>{props.sidebar}{props.children}{props.modal}</body></html>);
}
// app/@modal/login/page.tsx
export default function LoginModal() {return (<div className="modal"><h2>登录</h2>{/ 登录表单 /}</div>);
}
6. 拦截路由
6.1 照片预览示例
typescript
// app/photos/[id]/page.tsx
export default function PhotoPage({ params }: { params: { id: string } }) {return <h1>照片页面 {params.id}</h1>;
}
// app/photos/(.)[id]/page.tsx
export default function PhotoModal({ params }: { params: { id: string } }) {return (<div className="modal"><img src={/photos/${params.id}} alt="照片预览" /></div>);
}
7. 最佳实践
7.1 路由组织
app/
├── (auth)/
│ ├── login/
│ │ └── page.tsx
│ └── register/
│ └── page.tsx
├── (marketing)/
│ ├── about/
│ │ └── page.tsx
│ └── contact/
│ └── page.tsx
├── dashboard/
│ ├── layout.tsx
│ ├── page.tsx
│ └── settings/
│ └── page.tsx
└── page.tsx
7.2 类型安全路由
typescript
// lib/routes.ts
export const routes = {home: '/',auth: {login: '/login',register: '/register',},dashboard: {index: '/dashboard',settings: '/dashboard/settings',},blog: {index: '/blog',post: (id: string) => /blog/${id},},
} as const;
// 使用示例
import { routes } from '@/lib/routes';
import Link from 'next/link';
export function Navigation() {return (<nav><Link href={routes.home}>首页</Link><Link href={routes.auth.login}>登录</Link><Link href={routes.blog.post('123')}>博客文章</Link></nav>);
}
7.3 性能优化
typescript
// app/blog/[id]/page.tsx
import { Suspense } from 'react';
// 加载状态组件
function Loading() {return <div>加载中...</div>;
}
// 文章内容组件
async function BlogContent({ id }: { id: string }) {const post = await fetch(/api/posts/${id}).then(res => res.json());return <article>{post.content}</article>;
}
export default function BlogPost({ params }: { params: { id: string } }) {return (<div><h1>博客文章</h1><Suspense fallback={<Loading />}><BlogContent id={params.id} /></Suspense></div>);
}
7.4 错误处理
typescript
// app/error.tsx
'use client';
export default function Error({error,reset,}: {error: Error;reset: () => void;}) {return (<div className="error-container"><h2>出错了!</h2><p>{error.message}</p><button onClick={reset}>重试</button></div>);
}
8. 注意事项
- 页面组件必须是默认导出
- 布局组件会被其子路由共享
- 动态路由参数在构建时需要通过 generateStaticParams 生成
- 路由组不会影响 URL 结构
- 平行路由可以实现复杂的页面组合
- 使用 loading.tsx 和 error.tsx 提供更好的用户体验
- 合理使用路由组织可以提高代码可维护性
- 类型安全的路由可以减少运行时错误
9. 进阶技巧
- 使用中间件进行路由保护
- 实现渐进式路由加载
- 优化动态路由的静态生成
- 实现自定义 404 页面
- 集成认证系统
- 实现国际化路由
- 优化 SEO 配置
- 实现路由过渡动画
10. 总结
1.官网:https://www.nextjs.cn/docs/getting-started
2.