文章目录
- 1. 路由的基本概念
- 2. 客户端路由的核心
- (1) `useRouter`
- (2) `<Link />` 组件
- (3) `redirect()`
- 3. 动态路由与客户端获取参数
- 4. 客户端路由和服务端路由的区别
- 5. 路由案例
- 1. `app/layout.tsx` (全局布局)
- 2. `app/components/NavBar.tsx` (导航栏)
- 3. `app/page.tsx` (首页)
- 4. `app/blog/[id]/page.tsx` (详情页)
- 效果
1. 路由的基本概念
在Next.js 15(App Router 模式)中,路由主要依赖文件系统路由(File-system routing),所有页面都放在app/目录下:
app/ ├── page.tsx // 对应 "/" ├── about/ │ └── page.tsx // 对应 "/about" └── blog/ └── [id]/ └── page.tsx // 动态路由 "/blog/123"- 服务端渲染 (SSR):页面初次加载时由服务器返回 HTML。
- 客户端路由 (CSR):在页面之间跳转时,
Next.js使用客户端路由器(基于 React Router-like 机制)来实现无刷新切换。
2. 客户端路由的核心
客户端路由由next/navigation提供,主要用到两个Hook / API:
(1)useRouter
用于编程式导航,和 React Router 的useNavigate类似。
"use client";import{useRouter}from"next/navigation";exportdefaultfunctionHome(){constrouter=useRouter();return<button onClick={()=>router.push("/about")}>去 About 页面</button>;}router.push("/path"):跳转到某个页面,保留历史记录。router.replace("/path"):跳转但不保留历史记录。router.back():回退。
(2)<Link />组件
推荐使用<Link>来实现预加载和无刷新跳转。
importLinkfrom"next/link";exportdefaultfunctionNavBar(){return(<nav><Link href="/">首页</Link><Link href="/about">关于我们</Link></nav>);}<Link>默认会预加载目标页面的代码,提高切换速度。- 只有当目标在视口中可见时才会自动预加载。
(3)redirect()
用于服务端或客户端强制重定向。
import{redirect}from"next/navigation";exportdefaultfunctionPage(){constisLogin=false;if(!isLogin){redirect("/login");}return<div>已登录</div>;}3. 动态路由与客户端获取参数
动态路由文件夹[id]会捕获 URL 中的参数。
文件夹嵌套结构:app/blog/[id]/page.tsx如下:
app/ ├── blog/ │ └── [id]/ │ └── page.tsx // 动态路由 "/blog/123"组件获取如下:
// app/blog/[id]/page.tsx"use client";import{useParams}from"next/navigation";exportdefaultfunctionBlogPage(){constparams=useParams();// { id: "123" }return<h1>文章ID:{params.id}</h1>;}// 如果是 服务端渲染,直接通过 `params` 获取:exportdefaultfunctionBlogPage({params}:{params:{id:string}}){return<h1>文章ID:{params.id}</h1>;}上面这种方式只能获取一层动态路由参数,不能获取嵌套路由参数(例如/blog/123/comment/456就报错了)。
路由结构改用[...id]如下:
app/ ├── blog/ │ └── [...id]/ │ └── page.tsx // 动态路由 "/blog/123/comment/456"这样就可以获取到多层动态路由参数了。
还有一种情况我们想让这个路由参数是可选的,有没有都可以,那么在外层再包裹一层中括号变成了[[...id]],如下:
app/ ├── blog/ │ └── [[...id]]/ │ └── page.tsx // 动态路由 "/blog/123/comment/456"动态路由总结:
[id]:只能获取一层路由参数,例如/blog/123。[...id]:可以获取多层路由参数,例如/blog/123/comment/456。[[...id]]:路由参数是可选的,有没有都可以,例如/blog/123或/blog/123/comment/456。
4. 客户端路由和服务端路由的区别
| 特性 | 服务端渲染 (SSR) | 客户端路由 (CSR) |
|---|---|---|
| 初次加载 | 服务器生成 HTML | 前端只加载入口 HTML |
| 跳转 | 整页刷新 / SSR | 无刷新跳转,速度快 |
| 数据获取 | 通过fetch()/server actions | useEffect/ SWR / TanStack Query |
| SEO | 强 | 较弱(需 SSR/ISR 支持) |
Next.js 的优势就是两者结合—— 初次请求走 SSR,后续页面切换走 CSR。
5. 路由案例
整个案例包含:
- 首页
/:有一个跳转按钮,去详情页 - 详情页
/blog/[id]:显示文章 ID - 导航栏组件:全局无刷新跳转
项目结构
app/ ├── layout.tsx ├── page.tsx // 首页 ├── blog/ │ └── [id]/ │ └── page.tsx // 动态详情页 └── components/ └── NavBar.tsx // 导航栏1.app/layout.tsx(全局布局)
import"./globals.css";importNavBarfrom"./components/NavBar";exportdefaultfunctionRootLayout({children}:{children:React.ReactNode}){return(<html lang="en"><body><NavBar/><main>{children}</main></body></html>);}2.app/components/NavBar.tsx(导航栏)
"use client";importLinkfrom"next/link";exportdefaultfunctionNavBar(){return(<nav style={{display:"flex",gap:"20px",marginBottom:"20px"}}><Link href="/">首页</Link><Link href="/blog/123">文章123</Link><Link href="/blog/456">文章456</Link></nav>);}3.app/page.tsx(首页)
"use client";import{useRouter}from"next/navigation";exportdefaultfunctionHomePage(){constrouter=useRouter();return(<div><h1>首页</h1><button onClick={()=>router.push("/blog/999")}style={{marginTop:"20px"}}>去文章999</button></div>);}4.app/blog/[id]/page.tsx(详情页)
"use client";import{useParams,useRouter}from"next/navigation";exportdefaultfunctionBlogDetail(){constparams=useParams();// { id: "123" }constrouter=useRouter();return(<div><h1>文章详情</h1><p>当前文章ID:{params.id}</p><button onClick={()=>router.back()}>返回上一页</button></div>);}效果
打开
/→ 首页- 可以点击「去文章 999」按钮 → 跳转
/blog/999
- 可以点击「去文章 999」按钮 → 跳转
在导航栏点击
/blog/123或/blog/456→ 跳转不同文章详情在详情页点「返回上一页」 → 回到首页
🚀 感谢阅读!想了解更多?
📖 我的博客网站 | 记录思考,分享干货
🏡 我的个人主页 | 关于我、开源项目