【React】
-
-
-
-
- SPA的理解
- 路由
- react-router的理解
-
- react-router-dom(用于web)
- BrowserRouter和HashRouter
- [Link 和 Route](#Link 和 Route)
- Navlink
- Navlink的封装
- Switch和Routes(V5/V6区别)
- 路由组件和一般组件
- 多级路径页面刷新的样式丢失问题
- 路由的模糊匹配和精准匹配(V5/V6区别)
- 路由重定向Redirect和Navigate
- 嵌套路由
-
- 父组件
- 子组件
- 三级路由
- 路由向组件传递params参数(V6)
- 路由向组件传递search参数
- 路由向组件传递state参数
- [传递各种类型的 接收参数(V5版本)](#传递各种类型的 接收参数(V5版本))
- replace属性(替换当前路由)
- 编程式导航
- onClick的两种点击写法
- withRouter(V5)
-
- 解决方案
- [V6下移除了 withRouter](#V6下移除了 withRouter)
-
-
-
SPA的理解
- 单页Web应用
- 整个页面只有一个完整的页面,就一个.html
- 点击页面中的链接不会刷新页面只会做页面的局部更新
- 数据都需要ajax请求实现,并在前端展示
路由
- 一个路由就是一个映射关系(key:value)
- key为路径,value是function或components
后端路由
- 当value是function,一般是后端用以处理客户端提交的请求,当node接收到一个请求的时候,根据请求路径找到匹配的路由,调用路由的函数处理请求,返回响应数据
前端路由
- 当value是components,前端用于展示页面内容,游览器地址的变化成/test就是当前路由组件变为test组件
react-router的理解
- react的插件库
- 专门用来实现一个spa应用
- 下面又有三个用途web,native(原生),any(任何)
react-router-dom(用于web)
-
安装:npm i react-router-dom
-
BrowserRouter 或者HashRouter 包裹,鼓励 配合使用
-
Link 标签导航
-
内容route标签展示

BrowserRouter和HashRouter
1,底层原理不一致
- BrowserRouter使用的是h5的history API。不兼容IE9及以下版本
- HashRouter 使用的是URL的哈希值
2,url表现形式不一样
- BrowserRouter的路径中没有#,类似http://localhost:3000/tab2/nev1
- HashRouter的路径中包含#,类似http://localhost:3000/#/tab2/nev1
3,刷新后对路由state参数的影响
- BrowserRouter没有任何影响,因为state保存在history对象中
- HashRouter刷新后导致state参数的丢失
备注:HashRouter可以用于解决一些路径错误相关的问题
- 两者一般使用在包裹所有使用路由的地方,一般在index.js的App

Link 和 Route
javascript
import React from 'react'
import Main from './components/Main'
import Nev from './components/Nev'
import { Link, BrowserRouter, Route, Routes } from 'react-router-dom'
const App = () => {
return (
<BrowserRouter>
<div>
{/* 在react中靠路由链接实现切换组件 */}
<div>
<Link to="/main">main</Link>
<Link to="/nev">nev</Link>
</div>
<div>
{/* v6 中 <Route> 不再支持 component,必须改用 element 并传入组件实例(<Main /> 而非 Main) */}
<Routes>
<Route path="/main" element={<Main />} />
<Route path="/nev" element={<Nev />} />
</Routes>
</div>
</div>
</BrowserRouter>
)
}
export default App
Navlink
- 一个特殊版本的 Link,当它与当前 URL 匹配时,为其渲染元素添加样式属性。
- v5版本中支持 activeClassName/activeStyle
- v6 中 不再支持 activeClassName/activeStyle,而是通过 className 接收「回调函数」或「静态类名」,结合 isActive 状态控制激活样式。
javascript
<NavLink className={({ isActive }) => isActive ? 'newitem nav-link' : 'nav-link'} to="/nev">nev</NavLink>
javascript
.newitem{
color: aqua;
}

Navlink的封装
- 新建MyNavLink组件
javascript
import React from 'react'
import { NavLink } from 'react-router-dom'
const MyNavLink = (props) => {
const { to } = props
//实际上传递了children,children就是title
return (
<NavLink
className={({ isActive }) => (isActive ? 'newitem nav-link' : 'nav-link')}
to={to}
{...props}
>
</NavLink>
)
}
export default MyNavLink
- 使用
javascript
<div>
<NavLink className={({ isActive }) => isActive ? 'newitem nav-link' : 'nav-link'} to="/main">main</NavLink>
<NavLink className={({ isActive }) => isActive ? 'newitem nav-link' : 'nav-link'}to="/nev">nev</NavLink>
<MyNavLink to="tab1"></MyNavLink>
//实际上传递了children,children就是title
<MyNavLink to="tab2">tab2</MyNavLink>
</div>
<div>

Switch和Routes(V5/V6区别)
- 通常情况下,path和compent是一一对应的
- Switch可以提高路由匹配效率,单一匹配
v5版本下,必须通过添加Switch来实现,同一路径的多个 只会渲染第一个匹配的
- 不然,切换到nev的时候,两者都会展示
javascript
<Switch>
<Route path="/main" element={<Main />} />
<Route path="/nev" element={<Nev />} />
<Route path="/nev" element={<Main />} />
</Switch>
V6下,React Router v6 的 组件会按「最佳匹配 + 首次匹配优先」规则渲染路由,同一路径的多个 只会渲染第一个匹配的。
javascript
<Routes>
<Route path="/main" element={<Main />} />
<Route path="/nev" element={<Nev />} />
<Route path="/nev" element={<Main />} />
</Routes>
路由组件和一般组件
写法不同,
- 一般组件< Demo/ >
- 路由组件, <Route path="/main" element={} />
存储位置不同
- 一般组件放components
- 路由组件一般放pages
接收到的props不同
- 一般组件,写组件标签的时候传递啥,接收啥
- 路由组件,接收三个固定属性,history,location,match
v5:路由组件会自动接收 history/location/match 等 props;
v6:路由组件默认接收空 props,需通过 useNavigate/useLocation/useParams 等 Hooks 获取路由相关信息,或手动传递 props。
多级路径页面刷新的样式丢失问题
- 当路径增加前缀/pages的时候
- 根页面的页面静态资源丢失,原因:请求路径http://localhost:3000/css/bootstrap.css变成http://localhost:3000/pages/css/bootstrap.css,所以请求不到这个样式了
- -解决方法:
- 1,采用绝对路径,类似./css/bootstrap.css变成/css/bootstrap.css
- 2,采用%PUBLIC_URL%,
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css" />,只适用public文件下的 - 3,采用hash路由模式,HashRouter 替换BrowserRouter,/#/后的都是前端资源,刷新请求的时候路径不会带上/pages/,但不建议使用这个方式

路由的模糊匹配和精准匹配(V5/V6区别)
v5版本下
- 默认模糊匹配,顺序要一致,输入的路径必须包含匹配的路径
- 精准匹配模式,Route上exact={true}开启精准匹配
javascript
<Link to="/main/a/b">tab1</Link>
<Route exact path="/main" element={<Main />}
v6版本下
- React Router v6 的 默认采用「精确匹配」规则,
- v6 彻底抛弃了 v5 的「模糊匹配」,默认规则:
- 精确匹配:只有 URL 路径与 完全一致时才匹配(如 /nev 只匹配 /nev,不匹配 /nev/a/b);
- 子路径匹配:需通过「嵌套路由 + 」或「通配符 *」实现;
- 顺序无关:v6 按「路径精准度」匹配(而非定义顺序),更精准的路径优先。
javascript
// 所以下面的/main/a/b无法匹配上下面的main
<div>
<div>
<Link to="/main/a/b">tab1</Link>
<MyNavLink to="/nev">tab2</MyNavLink>
</div>
<div>
<Routes>
<Route exact={true} path="/main" element={<Main />} />
<Route path="/nev" element={<Nev />} />
</Routes>
</div>
</div>
路由重定向Redirect和Navigate
- 一般写在所有路由注册的最下方,当所有路由无法匹配的时候,跳转到Redirect指定的路由
V5版本下
javascript
<Route exact={true} path="/main" element={<Main />} />
<Route path="/nev" element={<Nev />} />
<Redirect to="/main"/>
V6版本下
javascript
<Routes>
<Route exact={true} path="/main" element={<Main />} />
<Route path="/nev" element={<Nev />} />
<Route path="*" element={<Navigate to="/main" replace />} />
</Routes>
嵌套路由
- 父路由(Tab2)路径:/tab2/*
- 子路由(Nev2)路径:nev2/(继承后为 /tab2/nev2/)
- 三级路由(Detail)路径:detail(继承后为 /tab2/nev2/detail)
父组件
javascript
import React from 'react'
import { Link, Route, Routes, Navigate } from 'react-router-dom'
import Tab1 from './components/tab1'
import Tab2 from './components/tab2'
const App = () => {
return (
<div>
{/* 编写路由链接 */}
<Link to="/tab1">tab1</Link>
<Link to="/tab2">tab2</Link>
<div>
{/* 注册路由 */}
<Routes>
<Route path="/tab1" element={<Tab1 />}></Route>
<Route path="/tab2/*" element={<Tab2 />}></Route>
<Route path="*" element={<Navigate to="/tab1" replace />} />
</Routes>
</div>
</div>
)
}
export default App
子组件
javascript
import React from 'react'
import Nev1 from './nev1'
import Nev2 from './nev2'
import {
Link,
NavLink,
BrowserRouter,
Route,
Routes,
Navigate,
} from 'react-router-dom'
const index = () => {
return (
<div>
<div>tab2内容</div>
{/* (即 /tab2/nev1/nev2)问题是 React Router 相对路径的核心特性导致的 */}
{/* 方案 1:使用绝对路径(最稳妥,避免路径叠加) */}
{/* 方案 2:使用 ../ 回到上一级(相对路径修正) */}
{/* 方案 3:使用 useNavigate 编程式导航(灵活控制) */}
<Link to="/tab2/nev1" style={{ marginRight: '10px' }}>
nev1
</Link>
<Link to="/tab2/nev2">nev2</Link>
<Routes>
{/* 首次进入 tab2 时默认显示 nev1 */}
<Route path="/" element={<Navigate to="nev1" replace />} />
<Route path="nev1" element={<Nev1 />}></Route>
<Route path="nev2/*" element={<Nev2 />}></Route>
</Routes>
</div>
)
}
export default index
三级路由
javascript
import { useState } from 'react'
import { Link, Route, Routes, Navigate } from 'react-router-dom'
import Detail from './detail'
const Nev2 = () => {
const [todos, setTodos] = useState([
{ id: 1, name: '乞力马扎罗', check: 1, route: '' },
{ id: 2, name: '打代码', check: 0, route: '' },
{ id: 3, name: '搞钱', check: 0, route: '' },
])
return (
<div>
nev2子级路由页面
{/* 编写路由链接 */}
{todos.map((item) => {
return (
<li key={item.id}>
<Link to="/tab2/nev2/detail" >
{item.name}
</Link>
</li>
)
})}
{/* 注册路由 */}
<Routes>
<Route path="detail" element={<Detail />}></Route>
</Routes>
</div>
)
}
export default Nev2

路由向组件传递params参数(V6)
javascript
import { useState } from 'react'
import { Link, Route, Routes, Navigate } from 'react-router-dom'
import Detail from './detail'
const Nev2 = () => {
const [todos, setTodos] = useState([
{ id: 1, name: '乞力马扎罗', check: 1, route: '' },
{ id: 2, name: '打代码', check: 0, route: '' },
{ id: 3, name: '搞钱', check: 0, route: '' },
])
return (
<div>
nev2子级路由页面
{/* 编写路由链接 */}
{/* 1,向路由组件传递params参数 */}
{todos.map((item) => {
return (
<li key={item.id}>
<Link to={`/tab2/nev2/detail/${item.id}/${item.name}`} >
{item.name}
</Link>
</li>
)
})}
{/* 注册路由 */}
{/* 2,声明接收params参数 */}
<Routes>
<Route path="detail/:id/:title" element={<Detail />}></Route>
</Routes>
</div>
)
}
export default Nev2
- detail.jsx
javascript
import React from 'react'
// 1. 引入 useParams 钩子
import { useParams } from 'react-router-dom'
function Detail() {
// 2. 调用 useParams 获取路由参数(与路由中声明的 :id/:title 对应)
const params = useParams()
// 3. 解构出 id 和 title(注意:参数名要和路由中声明的一致)
const { id, title } = params
// 打印参数(可在控制台查看)
console.log('接收的参数:', id, title)
{
/* nev路由传参-->详情页面接收参数 */
}
return <div>nev路由传参详情页面</div>
}
export default Detail

路由向组件传递search参数
javascript
{/* 向路由组件传递search参数 */}
<Link to={`/tab2/nev2/detail2/?id=${item.id}&title=${item.name}`}>
{item.name}详情2
</Link>
{/* 注册路由 */}
<Routes>
{/* search参数无需声明接收,正常注册路由即可*/}
<Route path="detail2" element={<Detail2 />}></Route>
</Routes>
- detail2.jsx
javascript
import React from 'react'
// 引入 useSearchParams 钩子
import { useSearchParams } from 'react-router-dom'
function Detail(props) {
// useSearchParams 返回数组:[查询参数对象, 修改参数的方法]
const [searchParams] = useSearchParams()
// 读取 id 和 title 参数(注意:获取的值是字符串类型)
const id = searchParams.get('id')
const title = searchParams.get('title')
console.log('Search 参数:', { id, title })
{
/* nev路由传参-->详情页面接收参数 */
}
return <div>nev路由传参详情页面2</div>
}
export default Detail
两者区别

路由向组件传递state参数
javascript
{/* 向路由组件传递state参数 */}
<Link
to="/tab2/nev2/detail3"
state={{
id: item.id,
name: item.name,
check: item.check,
}}
>
{item.name}详情3
</Link>
<Routes>
{/* state参数无需声明接收,正常注册路由即可*/}
<Route path="detail3" element={<Detail3 />}></Route>
</Routes>
- detail3.jsx
javascript
import React from 'react'
// 引入 useLocation 钩子
import { useLocation } from 'react-router-dom'
function Detail() {
// 接收 state 参数
const location = useLocation()
const state = location.state || {} // 防止无参数时报错
console.log('state 参数:',state)
{
/* nev路由传参-->详情页面接收参数 */
}
return <div>nev路由传参详情页面2</div>
}
export default Detail
传递各种类型的 接收参数(V5版本)

replace属性(替换当前路由)
编程式导航
javascript
import React from 'react'
import Nev1 from './nev1'
import Nev2 from './nev2'
import { Route, Routes,Navigate, useNavigate } from 'react-router-dom'
const Index = () => {
const navigate = useNavigate()
function nextPage(targetPath) {
// v5
// props.history.push(targetPath)
// v6
navigate(targetPath)
}
function nextPagereplace(targetPath) {
// v5
// props.history.replace(targetPath)
// v6
navigate(targetPath, { replace: true })
}
return (
<div>
<div>tab2内容</div>
<div>
<button onClick={() => nextPage('/tab2/nev1')}>跳转到nev1</button>
<button onClick={() => nextPagereplace('/tab2/nev2')}>跳转到nev2</button>
</div>
<Routes>
<Route path="/" element={<Navigate to="nev1" replace />} />
<Route path="nev1" element={<Nev1 />}></Route>
<Route path="nev2/*" element={<Nev2 />}></Route>
</Routes>
</div>
)
}
export default Index

前进和后退
javascript
import React from 'react'
import { Link, Route, Routes, Navigate, useNavigate } from 'react-router-dom'
import './App.css'
const App = () => {
const navigate = useNavigate()
function forward() {
navigate(1)
}
function back() {
navigate(-1)
}
return (
<div className="pageStyle">
<button onClick={back}>回退</button>
<button onClick={forward}>前进</button>
</div>
)
}
export default App
onClick的两种点击写法
- ✅ 无参函数推荐:onClick={back}(简洁 + 高性能);
- ✅ 有参函数推荐:onClick={() => nextPage(参数)}(灵活,日常开发首选);
- 优化点:如果箭头函数导致性能问题(如列表高频渲染),可改用 useCallback 缓存函数:
- ❌ 错误写法:onClick={back()}
加括号会导致组件渲染时立即执行 back(比如页面一加载就后退),而非点击时执行;

withRouter(V5)
- 是 React Router v4/v5 时代的核心高阶组件(HOC),核心作用是给非路由组件注入路由相关的 props
javascript
// 路由组件(直接被Route渲染,自带路由props)
const Home = (props) => {
console.log(props.history); // 能拿到
return <div>首页</div>;
};
// 普通组件(非Route渲染,无路由props)
const NavButton = (props) => {
console.log(props.history); // undefined
// 想点击跳转,但没有history.push方法
return <button onClick={() => props.history.push('/home')}>跳转首页</button>;
};
// 页面中使用
const App = () => {
return (
<Router>
<Route path="/home" component={Home} />
{/* NavButton是普通组件,无路由props */}
<NavButton />
</Router>
);
};
解决方案
javascript
import { withRouter } from 'react-router-dom';
// 用withRouter包裹普通组件
const NavButton = withRouter((props) => {
console.log(props.history); // 能拿到了
return <button onClick={() => props.history.push('/home')}>跳转首页</button>;
});
V6下移除了 withRouter
函数组件:直接使用 useNavigate/useLocation/useParams 等 Hooks(推荐):
javascript
import { useNavigate } from 'react-router-dom';
const NavButton = () => {
const navigate = useNavigate();
return <button onClick={() => navigate('/home')}>跳转首页</button>;
};
类组件:通过「函数组件包裹 + 传 props」的方式适配
javascript
import { useNavigate, useLocation } from 'react-router-dom';
// 封装HOC兼容类组件
const withRouter = (Component) => {
return (props) => {
const navigate = useNavigate();
const location = useLocation();
return <Component {...props} navigate={navigate} location={location} />;
};
};
// 类组件使用
class MyClassComponent extends React.Component {
render() {
return <div onClick={() => this.props.navigate('/home')}>跳转</div>;
}
}
export default withRouter(MyClassComponent);