主题
React Router 路由(一)
本页对应 React Router 路由,共 8 段视频,约 55 分钟。
学习目标
- 能够说明前端路由“路径与组件一一对应”的基本概念,并使用
createBrowserRouter配置path与element,通过RouterProvider注入路由实例,访问/login、/article验证对应内容渲染。 - 能够将路由配置从入口文件抽离为独立
router模块,创建页面级路由组件,并在入口中注入导出的路由实例,验证原路由功能保持不变。 - 能够分别使用
Link的to属性和useNavigate实现声明式、编程式导航跳转,并通过浏览器验证跳转结果。 - 能够使用
useSearchParams接收查询参数,使用useParams接收动态路由参数,并正确配置动态参数占位符,验证参数传递与读取结果。 - 能够使用
children配置嵌套路由、使用Outlet指定二级路由出口,并通过点击导航验证二级路由内容渲染。 - 能够配置
index默认二级路由和path: "*"的 404 兜底路由,并能够切换createBrowserRouter与createHashRouter,验证两种路由模式的 URL 表现。
知识点
先理解前端路由与最小环境
为什么需要: 前端路由解决“一个路径对应一个组件”的页面切换问题,访问 /login 渲染登录页,访问 /article 渲染文章页。
怎么想: 先不急着抽象,用最小 demo 跑通“路由表 + 注入路由实例”这条链路。
怎么做: 用 CRA 创建项目,安装核心包并启动:
bash
npx create-react-app react-router-pro
cd react-router-pro
npm i react-router-dom
npm start易错点: 包名是 react-router-dom,不是 react-router;安装后到 package.json 的 dependencies 里确认版本。
最小路由:path 与 element 对应
为什么需要: 路由系统要先声明“哪个路径渲染哪个内容”,否则浏览器地址变化时页面不知道渲染谁。
怎么想: 用 createBrowserRouter 接收数组,数组项里配置 path 和 element,再用 RouterProvider 把生成的 router 注入应用。
怎么做: 入口文件先写最小版:
jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
const router = createBrowserRouter([
{ path: '/login', element: <div>我是登录页</div> },
{ path: '/article', element: <div>我是文章页</div> },
])
// 用 RouterProvider 替换原来的 App
<RouterProvider router={router} />易错点: element 要写 JSX;RouterProvider 的 router 属性要绑定上面创建的实例。调试时手动输入 /login、/article,分别能看到对应内容即生效。
抽象路由模块:页面组件与 router 分离
为什么需要: 把页面内容直接写在入口文件不合理,实际项目要把页面级组件和路由配置拆开,后续加路由只改路由模块。
怎么想: pages 放路由级组件,router 放路由表,入口只负责注入。
怎么做:
jsx
// src/pages/Login/index.jsx
export default function Login() {
return <div>我是登录页</div>
}
// src/pages/Article/index.jsx
export default function Article() {
return <div>我是文章页</div>
}jsx
// src/router/index.jsx
import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login'
import Article from '../pages/Article'
const router = createBrowserRouter([
{ path: '/login', element: <Login /> },
{ path: '/article', element: <Article /> },
])
export default router入口改为导入 router,继续用 <RouterProvider router={router} />。
易错点: 页面组件放 pages,路由配置放 router;入口不要再手写路由表。刷新 /login、/article,效果应与最小版一致。
声明式与编程式导航
为什么需要: 多个路由不是孤立的,登录页需要跳到文章页,就要进行路由导航。
怎么想: 模板里用 Link 做声明式跳转;逻辑代码里用 useNavigate 做编程式跳转。
怎么做:
jsx
import { Link, useNavigate } from 'react-router-dom'
function Login() {
const navigate = useNavigate()
return (
<>
<Link to="/article">跳转到文章页</Link>
<button onClick={() => navigate('/article')}>跳转到文章页</button>
</>
)
}调试时点击 Link,地址变为 /article,内容切换;查看元素可见 Link 被解析为 a,href 是 to 的值。
易错点: to 要和路由表 path 对应;useNavigate 必须在组件内调用,不能在组件外调用。
查询字符串传参:useSearchParams
为什么需要: 跳转时可能要带参数,例如文章页需要 id 和 name。
怎么想: 查询字符串传参在路径后拼 ?id=1001&name=jack,接收时用 useSearchParams。
怎么做:
jsx
navigate('/article?id=1001&name=jack')
import { useSearchParams } from 'react-router-dom'
const [searchParams] = useSearchParams()
const id = searchParams.get('id')
const name = searchParams.get('name')调试时地址栏出现 ?id=1001&name=jack,目标页能渲染 1001 和 jack。
易错点: useSearchParams 返回数组,要解构;get 的参数名必须和拼接名一致。
动态路由传参:useParams
为什么需要: 查询字符串会带问号,有些场景希望路径更短,如 /article/1001。
怎么想: 路径参数要在路由表用 :id 占位,接收时用 useParams。
怎么做:
jsx
navigate('/article/1001')
// router
{ path: '/article/:id', element: <Article /> }
// Article
import { useParams } from 'react-router-dom'
const { id } = useParams()调试时 /article/1001 能匹配,页面拿到 1001;多个参数继续写 :name。
易错点: 路由表必须配置 :id 占位符,否则路径匹配不上;useParams 取出的属性名要和占位符一致。
嵌套路由与 Outlet
为什么需要: 后台管理常见“一级 Layout 内嵌二级页面”,点击菜单只切换右侧内容。
怎么想: 一级路由用 children 配二级路由,一级组件里用 <Outlet /> 决定二级路由渲染位置。
怎么做:
jsx
{
path: '/',
element: <Layout />,
children: [
{ path: 'board', element: <Board /> },
{ path: 'about', element: <About /> },
],
}jsx
// Layout
<Link to="/board">面板</Link>
<Link to="/about">关于</Link>
<Outlet />调试时点击“面板”“关于”,内容在 Outlet 位置切换。
易错点: 二级路由放在 children;Outlet 放哪里,二级内容就渲染在哪里。
默认二级路由与 404
为什么需要: 访问 / 时默认显示面板;用户输入错误路径时要有 404 兜底提示。
怎么想: 默认二级路由用 index: true 并去掉 path;404 在路由表末尾用 path: '*'。
怎么做:
jsx
{ index: true, element: <Board /> }
// 404 放数组末尾
{ path: '*', element: <NotFound /> }默认面板的 Link 改为 to="/"。调试刷新 / 能直接看到面板;手动输入 /a 显示 404。
易错点: index 路由不要再写 path;404 必须放末尾,星号表示通配兜底。
两种路由模式
为什么需要: 不同部署环境对 URL 和后端支持要求不同,需要选择 history 或 hash 模式。
怎么想: createBrowserRouter 是 history 模式,createHashRouter 是 hash 模式,其余配置不变。
怎么做:
jsx
import { createHashRouter } from 'react-router-dom'
const router = createHashRouter([...])调试时切换到 hash 后,地址会出现 #。
易错点: history 模式通常需要后端配合;hash 模式前端可独立处理,但 URL 带 #。替换 API 后不要改动其他路由配置。
前端路由到底在做什么
为什么需要:传统页面跳转往往要重新请求整页资源;而在单页应用里,我们希望地址变化时只切换页面中的某一块内容,用户体验更连贯。前端路由就是用来建立“地址”和“页面内容”之间对应关系的机制。
怎么想:把路由表理解成一张映射表:一个路径 path 对应一个组件 component。当浏览器访问某个 path 时,路由器就查表,把匹配到的组件渲染到页面中。比如访问 /login 渲染登录页,访问 /article 渲染文章页,这就是前端路由。
怎么做:在 React Router 中,路由表里通常写成 path 和 element 的对应关系。element 既可以放组件,也可以放常规 JSX 元素。访问不同路径时,React Router 会根据配置找到对应内容并渲染。

易错点:不要把“路径变化”理解成“整页刷新”;也不要以为 path 和组件是随便写的,二者必须在路由表里明确对应。路径匹配不到时,当前页面可能什么都不渲染,后续才需要用 404 兜底。
从零配置一个最小路由
为什么需要:只看概念不够,必须把路由系统跑起来,才能验证“访问路径 → 渲染组件”这件事真的生效。
怎么想:最小示例只需要两个路由:/login 和 /article。先创建路由实例,再用 RouterProvider 把实例注入应用入口。转写稿里的推导是:createBrowserRouter 负责接收路由数组并返回 router 实例;RouterProvider 负责把这个实例接入 React 应用。
怎么做:在入口文件中导入 createBrowserRouter 和 RouterProvider,配置路由并绑定:
jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
const router = createBrowserRouter([
{ path: '/login', element: <div>我是登录</div> },
{ path: '/article', element: <div>我是文章页</div> }
])
root.render(<RouterProvider router={router} />)调试时启动项目,在浏览器手动输入 /login,页面显示“我是登录”;再输入 /article,显示“我是文章页”,说明路由系统已生效。
易错点:element 里写的是 JSX,不是组件名本身;RouterProvider 的属性是 router,不要写成别的名字;路径要写清楚,如 /login,不要漏掉前面的斜杠。
把路由配置抽离成独立模块
为什么需要:最小示例把路由直接写在入口文件里,真实项目会让入口越来越乱。转写稿在视频末尾也提出:路由组件通常比较复杂,不应该长期用简单 <div> 直接写在入口里。
怎么想:把“页面级组件”放到 pages 目录,把“路由配置”放到 router 模块。入口文件只负责导入路由实例并注入 RouterProvider。
怎么做:先建页面组件:
jsx
// pages/Login/index.jsx
export default function Login() {
return <div>我是登录页</div>
}
// pages/Article/index.jsx
export default function Article() {
return <div>我是文章页</div>
}再建路由模块:
jsx
import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login'
import Article from '../pages/Article'
const router = createBrowserRouter([
{ path: '/login', element: <Login /> },
{ path: '/article', element: <Article /> }
])
export default router入口文件改为:
jsx
import { RouterProvider } from 'react-router-dom'
import router from './router'
root.render(<RouterProvider router={router} />)调试时刷新页面,再在 /login 和 /article 之间切换,功能应与之前一致,但结构更清晰。
易错点:组件要正确 export default,导入路径要对应;element 中要写 <Login />,不能写成 Login;抽离后入口文件不要再保留旧的路由配置,避免重复或冲突。访问未知路径时当前仍会空白,这属于后续 404 路由要解决的问题。
创建项目与安装依赖
为什么需要:先有一个能运行的路由环境,后续的跳转、传参、嵌套路由才有地方验证。怎么想:沿用 create-react-app 创建项目,再单独安装 React Router 的核心包。怎么做:执行 npx create-react-app react-router-pro,进入项目后安装 react-router-dom,再 npm start 启动。转写稿中安装后看到依赖为 6.9.0 版本。易错点:包名是 react-router-dom,不要漏掉 -dom;项目名不要写成中文或带空格。
bash
npx create-react-app react-router-pro
cd react-router-pro
npm install react-router-dom
npm start最小路由系统
为什么需要:先理解“一个 path 对应一个 component/element”,用最小 demo 把路由跑起来。怎么想:用 createBrowserRouter 配置路由表,用 RouterProvider 把 router 实例注入应用。怎么做:在入口文件中导入这两个 API,配置 /login 和 /article,element 先写简单 JSX。调试时手动输入 /login 显示“我是登录”,输入 /article 显示“我是文章页”,说明路由生效。易错点:React Router 配置项叫 element,不是 Vue 的 component;element 接收 JSX,不是字符串。
jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
const router = createBrowserRouter([
{ path: '/login', element: <div>我是登录</div> },
{ path: '/article', element: <div>我是文章页</div> }
])
root.render(<RouterProvider router={router} />)抽象路由模块
为什么需要:把页面内容直接写在入口文件里不科学,实际项目要拆分页面组件和路由配置。怎么想:页面级组件放到 pages,路由配置放到 router,入口只负责注入。怎么做:创建 pages/Login/index.js、pages/Article/index.js,在 router/index.js 中导入组件并配置 path/element,导出 router;入口使用 RouterProvider。刷新后 /login、/article 仍能正常显示。易错点:页面组件要导出并在路由文件中导入;入口不要重复配置路由表。
jsx
// router/index.js
import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login'
import Article from '../pages/Article'
export const router = createBrowserRouter([
{ path: '/login', element: <Login /> },
{ path: '/article', element: <Article /> }
])声明式与编程式导航
为什么需要:多个路由之间要能跳转。怎么想:模板中的菜单用 Link,逻辑代码中的跳转用 useNavigate。怎么做:Link 的 to 写目标路径;useNavigate 得到 navigate 方法,在事件中调用。调试时点击 Link,浏览器会把它解析成 <a>;点击按钮也能跳到文章页。易错点:Link 的 to 属性不能拼错;useNavigate 必须在组件内部调用。
jsx
import { Link, useNavigate } from 'react-router-dom'
const navigate = useNavigate()
<Link to="/article">跳转到文章页</Link>
<button onClick={() => navigate('/article')}>跳转到文章页</button>两种传参方式
为什么需要:跳转时经常要携带 ID、name 等参数。怎么想:查询字符串用 useSearchParams,路径参数用 useParams。怎么做:search 传参写成 /article?ID=1001&name=jack,接收时 params.get('ID');params 传参写成 /article/1001/jack,路由配置 article/:ID/:name,接收时 params.ID。调试中页面能显示 1001 和 jack。易错点:params 传参必须在路由 path 中写占位符,否则匹配不到;useSearchParams 得到的是对象,取值要调用 get。
jsx
navigate('/article?ID=1001&name=jack')
const [params] = useSearchParams()
const id = params.get('ID')
// 路由:{ path: 'article/:ID/:name', element: <Article /> }
const params = useParams()
const id = params.ID嵌套路由与默认二级路由
为什么需要:后台布局常是“一级 Layout + 二级内容区”。怎么想:一级路由用 children 配二级路由,一级组件中用 Outlet 标出口。怎么做:配置 / 渲染 Layout,children 配 board、about;Layout 中放 Link 和 Outlet。默认二级路由把 board 的 path 去掉,加 index: true,访问 / 时默认显示面板。易错点:忘记写 Outlet 时二级路由没有渲染位置;默认二级路由不要再保留原 path。
jsx
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Board /> },
{ path: 'about', element: <About /> }
]
}404 路由
为什么需要:用户输入错误地址时要有兜底提示。怎么想:在路由表末尾配置通配符 *。怎么做:准备 NotFound 组件,配置 { path: '*', element: <NotFound /> }。输入不存在的 /a 时显示“页面跑到月球了”。易错点:404 路由要放在数组末尾,否则可能抢先匹配。
jsx
{ path: '*', element: <NotFound /> }两种路由模式
为什么需要:不同部署环境对 URL 和后端配置要求不同。怎么想:history 模式地址干净但需后端支持;hash 模式地址带 #,前端可独立处理。怎么做:把 createBrowserRouter 换成 createHashRouter,其余配置不变,刷新后地址出现 #。易错点:切换模式只换创建 API,不要改路由表结构;实际选型要结合后端是否配合。
课件图示








代码示例
示例一:最小可运行的路由 demo(入口文件直接配置)
jsx
// src/index.jsx (CRA 项目入口,可直接覆盖 src/index.js 内容)
import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
// 第一步:创建 router 实例,并配置 path 与 element 的对应关系
const router = createBrowserRouter([
{
path: '/login',
element: <div>我是登录页</div>
},
{
path: '/article',
element: <div>我是文章页</div>
}
])
// 第二步:用 RouterProvider 组件把 router 实例注入到应用中
ReactDOM.createRoot(document.getElementById('root')).render(
<RouterProvider router={router} />
)关键行与易错点
createBrowserRouter是一个方法(返回 router 实例),RouterProvider是一个组件,两者都从react-router-dom导入,不要漏掉-dom。- 路由表中用的是
path+element,不是 Vue 里的component;element既可以写组件<Login />,也可以写普通 JSX 元素<div>。 RouterProvider的属性名固定是router(不是routes),值必须是createBrowserRouter的返回值。- 原来的
<App />要移除,否则路由系统不会生效;访问地址需手动输入/login、/article验证。 - 这种“把路由写进入口文件”的写法只适合演示,实际开发要抽成独立模块(见示例二)。
示例二:抽象路由模块 + 嵌套路由 + 默认二级路由 + 404
jsx
// src/pages/Layout/index.jsx —— 一级路由组件(含二级路由出口)
import { Link, Outlet } from 'react-router-dom'
export default function Layout() {
return (
<div>
<div>我是一级路由 Layout</div>
<Link to="/">面板</Link>
<Link to="/about">关于</Link>
{/* 二级路由的渲染位置,写在哪里就渲染在哪里 */}
<Outlet />
</div>
)
}jsx
// src/pages/Board/index.jsx
export default function Board() {
return <div>我是面板</div>
}
// src/pages/About/index.jsx
export default function About() {
return <div>我是关于</div>
}
// src/pages/NotFound/index.jsx
export default function NotFound() {
return <div>页面跑到月球了</div>
}jsx
// src/router/index.jsx —— 路由模块,统一管理路由表
import { createBrowserRouter } from 'react-router-dom'
import Layout from '../pages/Layout'
import Board from '../pages/Board'
import About from '../pages/About'
import NotFound from '../pages/NotFound'
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
// children 配置嵌套(父子)关系
children: [
// index: true 表示默认二级路由,访问 "/" 时默认渲染 Board
{ index: true, element: <Board /> },
// 子路由 path 不要加前导斜杠
{ path: 'about', element: <About /> }
]
},
// 404 兜底必须放在数组末尾,* 是通配符
{ path: '*', element: <NotFound /> }
])
export default routerjsx
// src/index.jsx —— 入口只负责注入 router
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from './router'
ReactDOM.createRoot(document.getElementById('root')).render(
<RouterProvider router={router} />
)关键行与易错点
- 页面级组件放在
src/pages(Vue 里常叫views),每个组件一个文件夹,方便后续加样式文件;路由表放src/router,入口只保留RouterProvider。 - 嵌套路由靠
children属性;子路由的path不要写成/about,写相对路径about,否则匹配不到。 - 二级路由的出口是
<Outlet />,忘记写它则子路由内容无处渲染,页面看起来“跳转没反应”。 - 默认二级路由:把
path去掉,改为index: true;同时Layout里指向面板的Link to="/board"必须改成to="/",否则会走到 404。 - 404 的
path: '*'必须放在路由数组最后,因为匹配是从上到下的兜底逻辑。
示例三:路由导航跳转与两种传参方式
jsx
// src/pages/Login/index.jsx —— 声明式导航 + 编程式导航
import { Link, useNavigate } from 'react-router-dom'
export default function Login() {
// 钩子只能在组件内部调用,返回一个跳转方法
const navigate = useNavigate()
return (
<div>
<div>我是登录页</div>
{/* 声明式导航:to 是固定属性,写目标路由 path */}
<Link to="/article">跳转到文章页</Link>
{/* searchParams 传参:路径后用 ? 拼接,多个参数用 & 连接 */}
<Link to="/article?id=1001&name=jack">searchParams 传参</Link>
{/* params 传参:直接拼在路径上,没有问号 */}
<Link to="/article/1001/jack">params 传参</Link>
{/* 编程式(命令式)导航,适合登录成功后的逻辑跳转 */}
<button onClick={() => navigate('/article/1001/jack')}>命令式跳转</button>
</div>
)
}jsx
// src/pages/Article/index.jsx —— 接收参数
import { useSearchParams, useParams } from 'react-router-dom'
export default function Article() {
// searchParams 取值:返回数组,解构出 params 对象,再用 get 方法取
const [searchParams] = useSearchParams()
const id = searchParams.get('id')
const name = searchParams.get('name')
// params 取值:返回对象,直接点语法
const { id: pathId, name: pathName } = useParams()
return (
<div>
<div>我是文章页</div>
<div>searchParams 的 id:{id}</div>
<div>searchParams 的 name:{name}</div>
<div>params 的 id:{pathId}</div>
<div>params 的 name:{pathName}</div>
</div>
)
}jsx
// src/router/index.jsx —— 注意 params 传参必须配置占位符
import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login'
import Article from '../pages/Article'
import NotFound from '../pages/NotFound'
const router = createBrowserRouter([
{ path: '/login', element: <Login /> },
{ path: '/article', element: <Article /> },
// 冒号后是占位符名字,和 useParams 解构的名字一一对应
{ path: '/article/:id/:name', element: <Article /> },
{ path: '*', element: <NotFound /> }
])
export default router关键行与易错点
- 声明式导航用
<Link to="路径">,渲染后会被解析成<a>标签,to的值必须与路由表里的path对应。 - 编程式导航必须用
useNavigate(),它只能在组件函数体内调用,写在组件外面会报 Hook 调用错误。 useSearchParams的返回值是数组(类似useState),要写成const [params] = useSearchParams(),取参数用params.get('id');少写中括号直接调用.get会报错。useParams的返回值是对象,直接点语法取值,但前提是路由表里配了:id这样的占位符——漏配占位符是 params 传参最常见的失败原因。- 两种传参方式的路由配置不同:
/article?id=1001匹配path: '/article';/article/1001/jack必须匹配path: '/article/:id/:name',路径段数不一致就落到 404。
常见错误
1. 页面白屏,控制台报 “useNavigate() may be used only in the context of a <Router>”
- 现象:组件一渲染就报错崩溃,或点击按钮无反应。
- 原因:入口文件里仍然渲染的是
<App />,<RouterProvider>没挂上;或者把useNavigate/useSearchParams这类钩子写在了被RouterProvider包裹的层级之外(例如入口里直接调用)。 - 修法:入口只保留
<RouterProvider router={router} />,把需要导航逻辑的代码挪进路由表element指向的组件内部。
2. element 写成 {Login} 或 Login(),组件不渲染 / 报错
- 现象:路径匹配上了,但页面空白或抛异常。
- 原因:
element接收的是 JSX 节点,不是组件引用,也不是函数调用结果。 - 修法:统一写
<Login />;同时检查是不是把component这个 Vue 习惯的字段名写了进来。
3. 点击链接跳不过去,或跳过去是空白页
- 现象:URL 变了,但内容没渲染,或直接落到 404。
- 原因:
path少写前导/;Link的to与path大小写、层级不一致;嵌套子路由的path写成了/board这种绝对路径导致匹配不上。 - 修法:一级
path统一以/开头,子路由path不写前导斜杠,to与path逐字符对齐后再测。
4. 路径传参时 params.id 恒为 undefined
- 现象:
useParams()拿到空对象。 - 原因:路由表里忘了加
:id占位符;或占位符名字与解构出来的字段名不一致(大小写也算)。 - 修法:
path: '/article/:id'与const { id } = useParams()严格同名;这类传参“占位符”是必做步骤,不能只改跳转地址。
5. 查询串传参取不到值
- 现象:
searchParams.get('id')返回null。 - 原因:跳转地址写成了
/article/1001(路径风格),接收端却用useSearchParams;或拼参数时漏了?、多个参数之间漏了&。 - 修法:先确定用哪条传参路线,传和收必须成对;
?a=1&b=2配useSearchParams,/article/1001配useParams。
6. 嵌套路由点击菜单,二级内容不显示
- 现象:一级组件渲染了,二级出口处始终空白。
- 原因:一级组件里漏了
<Outlet />;或把children写在了错误的对象层级上。 - 修法:在一级组件需要出内容的位置放
<Outlet />,并确认children数组挂在对应的一级路由对象内。
7. 配了默认二级路由后,点击菜单报错或路径重复
- 现象:点“面板”跳到
/board/board或直接 404。 - 原因:改成默认路由后仍保留了
path;或Link的to没跟着改。 - 修法:默认路由删掉
path、只留index: true,对应链接地址同步改成一级路径/。
8. 输入错误地址仍白屏,404 组件不生效
- 现象:随便敲个
/xxx什么都没有。 - 原因:
path: '*'没放在路由数组最后一项,被前面的规则抢先;或写进了children里。 - 修法:
*永远放路由表末尾,作为兜底项。
练习
练习 1|环境与最小路由 用 CRA 建项目并安装 react-router-dom,实现 /login、/article 两条路由,手动改地址栏验证渲染;然后把两个页面拆成独立组件文件,新建 router/index.js 统一配置,入口只留 RouterProvider。 验收:新增第三个页面时,只需新建组件 + 改 router/index.js 一处。
练习 2|两种导航方式 在登录页放一个 <Link> 和一个按钮,两者都跳 /article;按钮用 useNavigate 实现,并在跳转前 console.log 一行模拟“登录成功”。 验收:能说清两段代码分别放在模板里还是逻辑里,以及各自适合什么场景。
练习 3|查询串传参 从登录页跳到 /article?tab=hot&id=1001,在文章页用 useSearchParams 取出两个值并渲染;再额外做一个“切到 tab=new”的按钮,用 setSearchParams 改参数。 验收:刷新页面后参数不丢,get 取到的值与地址栏一致。
练习 4|路径传参 把练习 3 改成 /article/:id/:name 形式,同步改路由表占位符、跳转地址、接收端。 验收:故意把占位符名字改错一次,观察 undefined 现象并改回。
练习 5|后台布局 + 兜底 搭一个 / 的 Layout 一级路由,内含 board、about 两个二级路由,用 <Outlet /> 做出口、用 <Link> 做菜单;把 board 设为默认二级路由;最后加 * 兜底 404 组件。 验收:访问 / 直接看到面板内容;点菜单 URL 与内容同步变化;输入 /abc 落到 404;把 createBrowserRouter 换成 createHashRouter 后,其余代码一行不改仍能正常工作。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | ReactRouter-快速开始 | 09:34 |
| 2 | ReactRouter-抽象路由模块 | 07:30 |
| 3 | ReactRouter-路由导航跳转 | 07:48 |
| 4 | ReactRouter-导航跳转传参 | 09:41 |
| 5 | ReactRouter-嵌套路由配置 | 09:13 |
| 6 | ReactRouter-默认二级路由配置 | 03:24 |
| 7 | ReactRouter-404路由配置 | 04:37 |
| 8 | ReactRouter-俩种路由模式 | 03:16 |
小结
- React Router v6 的接入永远是“创建实例 → 注入 Provider”两步,实例是唯一入口,不要在入口再渲染业务根组件。
- 路由表必须独立成模块,页面组件放
pages/views,新增页面只改这一处,这是可维护性的分水岭。 element收 JSX 节点,不是组件引用——这是从 Vue 迁移过来最容易踩的第一个坑。- 传参有两条互斥路线,传与收必须成对;其中路径传参多一步“路由表加占位符”,漏了必出
undefined。 - 嵌套路由是
children+<Outlet />+ 子路由相对路径三件套,缺任何一件二级内容都不显示。 - 默认二级路由用
index: true且不能带path,改完记得同步修改指向它的链接地址。 path: '*'的优先级由数组顺序决定,兜底路由永远放最后。- 声明式用于模板与菜单,编程式用于逻辑之后(登录成功、请求回调);history 与 hash 只差一个创建 API,选型取决于后端是否配合。