React 路由是构建单页应用(SPA)时管理页面导航和视图切换的核心工具。
什么是单页面应用?
SPA:单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。
优点:
具有桌面应用的即时性、网站的可移植性和可访问性
用户体验好、快,内容的改变不需要重新加载整个页面,局部刷新
缺点:
不利于SEO优化
首次渲染速度慢
为什么需要路由?
单页应用(SPA)中通过 URL 映射不同组件,实现无刷新页面切换。
保持浏览器历史记录同步(前进/后退)。
支持动态路由、嵌套路由、权限控制等场景。
基础用法
路由配置
// App.jsx
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './Home';
import About from './About';
import User from './User';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} />
<Route path="*" element={<NotFound />} /> {/* 404 页面 */}
</Routes>
</Router>
);
}
导航组件
<Link>
:替代 <a>
标签,无刷新跳转
import { Link } from 'react-router-dom';
<Link to="/about">关于我们</Link>
<NavLink>
:高亮当前活动链接
<NavLink to="/" style={({ isActive }) => ({ color: isActive ? 'red' : 'black' })}>
首页
</NavLink>
编程式导航
import {useNavigate} from 'react-router-dom'
function LoginButton() {
const navigate = useNavigate();
return (
<button onClick={() => navigate('/about')}>
登录后跳转
</button>
);
}
//navigate(跳转的路径,携带参数的对象) params和search参数直接拼接路径后,state传参需要对象
navigate('detail',{replace:false,state:{id:xxx,message:xxxx})
navigate(1)前进1 navigate(-1)后退1
路由v5版本与v6版本对比
1.routes代替了switch,Navigate替代了Redirect,element替代了component
2.不用Route标签包裹子组件,可以直接使用element属性,并且不需要exact来表示精准匹配,V6版本内部算法改变,它默认就是匹配完整路径,先后顺序不再重要,它能够自动找出最优匹配路径
//v5版本
<NavLink activeClassName='xxxxx' to='/home'>xxx</NavLink>
<switch>
<Route path='/' component={Home}/>
<Route path='/home' component={Home}/>
<Route path='/about' component={About}/>
<Route component={NotFound}/>
</switch>
//v6版本
<NavLink className={({isActive})=>isActive?'xxx':''} to='/home'>xxx</NavLink>
<Routes>
<Route path='/' element={<Navigate to='/home' replace={false}/>}/>//replace控制跳转模式,push或者
replace,默认push
<Route path='/home' element={<Home/>} caseSensitive={false}/> //caseSensitive路由匹配区分大小写,
默认false
<Route path='/about' element={<About/>}> //嵌套路由
<Route path='/about/news' element={<News/>}/>
<Route path='/about/message' element={<Message/>}/>
</Route>
</Routes>
函数组件路由表
//函数组件里 单独维护一个routes的文件夹,方便路由的扩展
export default [
{path:'/',element:<Navigate to='/home' />},
{
path:'/home',
element:<Home/>
},
{
path:'/about',
element:<About/>,
children: //嵌套子路由
[
{path:'news',element:<News/>},
{path:'message',element:<Message/>}
]
},
]
import {Link,NavLink,useRoutes} from 'react-router-dom'
export default function App() {
const element= useRoutes(router) //router引入的路由链表
return (
<div>
<div>
<Link to='/home'>Home</Link> |
<Link to='/about'>About</Link>
</div>
<div>
{element}
</div>
</div>
)
}
子路由占位符Outlet
//v6版本使用 Outlet组件,此组件是一个占位符,告诉 React Router嵌套的内容应该放到哪里
//子路由匹配路径问题这几种写法等价
//<NavLink to='news'>news</NavLink>
//<NavLink to='./news'>news</NavLink>
//<NavLink to='/about/news'>news</NavLink>
// 父路由
<Route path="/about" element={<About/>}>
{/* 子路由路径会拼接父路径 */}
<Route path="news" element={<News/>} />
<Route index element={<NewsHome/>} /> {/* 默认子路由 */}
</Route>
import { NavLink,Outlet } from 'react-router-dom'
export default class About extends Component {
render() {
return (
<div>
About组件
<div>
<NavLink to='news'>news</NavLink> | <NavLink to='message'>message</NavLink>
</div>
<div>
<Outlet/>
</div>
</div>
)
}
}
路由传参
params传参
路径参数:/user/:id
→ useParams()
//路由表
{
path:'/about',
element:<About/>,
children:
[
{path:'/about/news',element:<News/>},
{path:'/about/message',element:<Message/>, children:
[{path:'detail/:id/:message',element:<Detail/>}]
}
]
},
export default class news extends Component {
state={
arrList:[{id:1,message:'message1'},
{id:2,message:'message2'},
{id:3,message:'message3'}]
}
render() {
return (
<div>
<ul>
{this.state.arrList.map(item=>(
<li key={item.id}>
<Link to={`detail/${item.id}/${item.message}`}>{item.message}</Link>
</li>
))}
</ul>
<div>
<Outlet/>
</div>
</div>
)
}
}
//useMatch也可以拿到params参数 写法 useMatch('/About/messagedetail/:id/:message/')参数是完整路径
import React from 'react'
import {useParams} from 'react-router-dom'//useParams拿到传递值得对象
export default function detail() {
const {id,message}= useParams()
return (
<ul>
<li>message编号为:{id}</li>
<li>message信息为:{message}</li>
</ul>
)
}
查询参数:/search?q=react
→ useSearchParams()
//路由表
{
path:'/about',
element:<About/>,
children:
[
{path:'/about/news',element:<News/>},
{path:'/about/message',element:<Message/>, children:
[{path:'detail',element:<Detail/>}]
}
]
}
export default class news extends Component {
state={
arrList:[{id:1,message:'message1'},
{id:2,message:'message2'},
{id:3,message:'message3'}]
}
render() {
return (
<div>
<ul>
{this.state.arrList.map(item=>(
<li key={item.id}>
<Link to={`detail?id=${item.id}&message=${item.message}`}>{item.message}</Link>
</li>
))}
</ul>
<div>
<Outlet/>
</div>
</div>
)
}
}
import React from 'react'
import {useSearchParams} from 'react-router-dom'
export default function detail() {
const [search,setSearch]= useSearchParams();
//要获取值search.get('id') ,setSearch改为对应的传参,例如setSearch('id=4&message=嘻嘻')
const id=search.get('id');
const message=search.get('message')
return (
<ul>
<li>message编号为:{id}</li>
<li>message信息为:{message}</li>
</ul>
)
}
state传参
状态传递:navigate('/detail', { state: { data } })
→ useLocation().state
//路由表
{
path:'/about',
element:<About/>,
children:
[
{path:'/about/news',element:<News/>},
{path:'/about/message',element:<Message/>, children:
[{path:'detail',element:<Detail/>}]
}
]
}
export default class news extends Component {
state={
arrList:[{id:1,message:'message1'},
{id:2,message:'message2'},
{id:3,message:'message3'}]
}
render() {
return (
<div>
<ul>
{this.state.arrList.map(item=>(
<li key={item.id}>
<Link to='detail' state={{
id:item.id,
message:item.message
}}>{item.message}</Link>
</li>
))}
</ul>
<div>
<Outlet/>
</div>
</div>
)
}
}
import {useLocation} from 'react-router-dom'
export default function detail() {
const {state:{id,message}}= useLocation();
return (
<ul>
<li>message编号为:{id}</li>
<li>message信息为:{message}</li>
</ul>
)
}
路由守卫(权限控制)
// 自定义 ProtectedRoute 组件
function ProtectedRoute({ children }) {
const isAuthenticated = checkAuth(); // 你的权限验证逻辑
const navigate = useNavigate();
useEffect(() => {
if (!isAuthenticated) {
navigate('/login');
}
}, [isAuthenticated, navigate]);
return isAuthenticated ? children : null;
}
// 使用方式
<Route
path="/admin"
element={
<ProtectedRoute>
<AdminPanel />
</ProtectedRoute>
}
/>
懒加载路由
import { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./HeavyComponent'));
<Route
path="/heavy"
element={
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
}
/>