React--Router(6.x版本)
路由概念
- 核心: 改变
URL,但是页面不进行整体的刷新 - 作用:维护
URL和渲染页面的映射关系 - 目的: 通过 JavaScript 监听
URL的改变,并且根据URL的不同重新渲染页面
改变
URL但不刷新页面的两种方式
- 通过
URL的hash改变URL - 通过
HTML5的history模式改变URL
Hash模式
- 使用
URL的hash来模拟一个完整的URL,当hash值改变时,页面不会重新加载 URL的hash也就是锚点(#),本质上是改变window.location的hash属性hash模式的原理是通过监听浏览器的hashchange事件,根据不同的值渲染不同的内容
<a href="#/home">home</a>
<a href="#/user">user</a>
<h1 class="router-view">Default</h1>
const routerViewEl = document.querySelector('.router-view')
window.addEventListener('hashchange', function () {
switch (location.hash) {
case '#/home':
routerViewEl.innerHTML = 'Home';
break
case '#/user':
routerViewEl.innerHTML = 'User';
break
default:
routerViewEl.innerHTML = 'Default';
}
})
hash的优势就是兼容性更好,在老版IE中都可以运行,但缺陷是有#,显得不像真实路径
history模式
history接口是HTML5新增的,提供了对历史记录修改的功能
六种改变
URL而不刷新页面的模式
replaceState: 替换原来的路径(不保留历史记录)pushState: 追加新的路径(类似入栈)popState: 路径回退(类似出栈)go: 向前或向后改变路径forward: 向前改变路径back: 向后改变路径
<a href="/home">home</a>
<a href="/user">user</a>
<h1 class="router-view">Default</h1>
function historyChange() {
switch (location.pathname) {
case '/home':
routerViewEl.innerHTML = 'Home';
break
case '/user':
routerViewEl.innerHTML = 'User';
break
default:
routerViewEl.innerHTML = 'Default';
}
}
window.addEventListener('popstate', historyChange) // 监听点击浏览器的前进后退按钮
const routerViewEl = document.querySelector('.router-view')
const linkEls = document.getElementsByTagName('a')
for (const linkEl of linkEls) {
linkEl.addEventListener('click', function (e) {
e.preventDefault()
const hrefValue = this.getAttribute('href')
history.pushState({}, '', hrefValue) // 改变url
historyChange()
})
}
- 监听浏览器的
popstate事件,但调用history.pushState()或history.replaceState()并不会使其触发, 只有点击浏览器的回退或前进按钮才会触发该事件
基本使用
- 选择
react-router-dom,因为react-router会包含react-native的内容,web开发并不需要
npm i react-router-dom
react-router-dom最主要的API是提供的组件
-
BrowserRouter组件: 使用
history模式 -
HashRouter组件: 使用
hash模式 -
Router中包含对路径改变的监听,并且会将相应的路径传递给子组件
import { HashRouter } from 'react-router-dom';
<HashRouter>
<App />
</HashRouter>
映射配置
- Routes组件: 包裹所有的
Route,在其中配置路由的映射关系(Router5.x使用的是Switch组件) - Route组件:
Route用于路径的匹配
Route组件的常用属性
- path: 用于设置匹配到的路径
- element: 匹配到对应路径后需要渲染的组件(
Router5.x使用的是component属性) - exact: 精准匹配,只有精准匹配到完全一致的路径才渲染对应组件(
Router6.x不再支持该属性)
import { Route, Routes } from 'react-router-dom';
{/* 映射关系:path => Component */}
<div className="content">
{/* 映射关系:path => Component */}
<Routes>
<Route path='/' element={<Home/>} />
<Route path='/home' element={<Home/>} />
<Route path='/about' element={<About/>} />
</Routes>
</div>
跳转配置
- Link: 通常路径的跳转是使用
Link组件,最终会被渲染成a元素
Link组件的常用属性
- to:
Link中最重要的属性,用于设置跳转的目标路径 - replace: 使用目标路径直接替换当前路径
- reloadDocument: 是否重新加载页面,默认
false
import { Link } from 'react-router-dom';
<div className="link-box">
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
</div>
- NavLink: 在
Link基础之上增加了一些样式属性
NavLink组件的常用属性
- style: 传入函数,函数有一个为对象的参数,其中包含
isActive属性 - className: 和
style属性同理
import { NavLink } from 'react-router-dom';
const getActiveStyle = ({isActive}) => {
return { color: isActive ? 'red' : '' }
};
const getActiveClass = ({isActive}) => {
return isActive ? 'link-active' : ''
};
<div className="link-box">
<NavLink to="/home" style={this.getActiveStyle}>Home</NavLink>
<NavLink to="/about" className={this.getActiveClass}>About</NavLink>
</div>
NavLink也可以对激活时添加的active类自定义样式

.link-box {
.active {
color: red;
font-size: 25px;
}
}

Navigate导航
Navigate用于路由的重定向,使用该组件就会跳转到对应的to路径中(Router5.x使用的是Redirect)
import { Navigate } from 'react-router-dom';
<Navigate to='/home'/>
- 当用户第一次访问页面时,可以使用
Navigate组件作重定向操作
import { Routes, Route, Navigate } from 'react-router-dom';
<Routes>
<Route path='/' element={<Navigate to='/home' />}/>
<Route path='/home' element={<Home/>} />
</Routes>
Not Found配置
- 当访问不存在的
URL时,使用*通配符匹配一个404兜底路由
<Route path='*' element={<NotFound/>} />

路由嵌套
- 在开发中路由之间是存在嵌套关系的,可以使用
<Route>组件以嵌套的形式配置
import { Routes, Route, Navigate } from 'react-router-dom';
<Routes>
<Route path='/home' element={<Home/>} >
{/* 配置子路由 */}
<Route path='/home' element={<Navigate to='/home/recommend' />} />
<Route path='/home/recommend' element={<HomeRecommend/>} />
<Route path='/home/ranking' element={<HomeRanking/>} />
</Route>
</Routes>
- 使用
<Outlet>组件,用于在父路由元素中作为子路由的占位元素
// Home.jsx
import { NavLink, Outlet } from 'react-router-dom';
<div>
<h1>Home Page</h1>
<div className='home-nav'>
<NavLink to="/home/recommend">推荐</NavLink>
<NavLink to="/home/ranking">排行榜</NavLink>
</div>
{/* 占位 */}
<Outlet/>
</div>

编程式路由
- 路由可以通过
<Link>或者<NavLink>进行跳转,也可以通过JavaScript代码进行跳转 - 在
Router6.x版本之后,编程式路由的API都迁移到了Hooks的写法 - 可以通过
react-router-dom提供的useNavigate的Hook获取到navigate函数进行操作
const App = memo((props) => {
const navigate = useNavigate()
return (
<AppWrapper>
<button onClick={e => navigate('/category')}>分类</button>
<button onClick={e => navigate('/order')}>订单</button>
</AppWrapper>
)
})
- 注意:
Router6.x版本提供的Hook只能在函数式组件中使用,类组件中则无法使用 - 如果想要在类组件中使用
Hook,可以通过高阶组件进行封装,将hook传到组件的props上
import { useNavigate } from 'react-router-dom';
export default function withRouter(Component) {
return function(props) {
const navigate = useNavigate();
return <Component {...props} router={{navigate}}/>
}
}
import { useNavigate } from 'react-router-dom';
import withRouter from '../hoc/withRouter';
class Home extends PureComponent {
render() {
const { navigate } = this.props.router;
return <button onClick = {e => navigate('/home/songMenu')}>歌单</button>
}
}
export default withRouter(Home)
navigate函数可以接收三个参数
- to: 跳转的目标路径(必传)
- options: 对象,
{replace: boolean, state: any},使用replace可更改跳转方式 - delta: 步数,如
navigate(1)前进一步
参数传递
React中通过路由传递参数有两种方式
- 通过动态路由的方式(params)
- 通过查询字符串传递参数(query)
动态路由(params)
- 动态路由概念: 路由中的路径并不固定,如
/detail/:id,那么/detail/abc、/detail/123都可以匹配到该Route,这种匹配规则称为动态路由 - 通常情况下,使用动态路由可以为路由传递参数
<Route path='/detail/:id' element={<Detail/>} />
- 在点击跳转时可以在路径后携带
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
<button onClick={e => navigate(`/detail/${id}`)}>详情</button>
- 获取方式则是通过
useParams这个hook
import { useParams } from 'react-router-dom';
const params = useParams();
<h2>id:{params.id}</h2>
查询字符串(query)
- 通过查询字符串传参,则是在跳转对应
URL拼接参数,如/detail?id=111&name=tony,路由配置仍然是固定写法
<Route path='/detail' element={<Detail/>} />
- 在点击跳转时可以在路径后拼接需要传递的参数
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
<button onClick={e => navigate(`/detail/?id=${id}`)}>详情</button>
- 可以通过
useSearchParams这个hook获取
useSearchParams返回一个数组,数组中携带两个元素
- searchParams: 携带路由参数的键值对列表
- setSearchParams: 修改路由参数的方法
const [ searchParams, setSearchParams ] = useSearchParams();
const query = Object.fromEntries(searchParams); // 转化为普通对象
<h2>id:{query.id}</h2>
- 也可以通过
useLocation这个hook获取,但获取到的形式为"?id=111",需要自行处理
const { search } = useLocation(); // 返回一个location对象,路由传递的参数在search属性中
const query = { id: search.split('=').pop() }; // 处理参数
<h2>id:{query.id}</h2>
配置文件
- 如果将路由的映射配置都使用
<Route>写在组件内部,那么路由会变得非常混乱 - 可以使用
routes映射配置文件,结合useRoutes进行渲染,但早期的Router并没有提供相关的API,需要借助于react-router-config完成 - 路由映射文件如下,类似
VueRouter的配置
const routes = [
{
path: '/',
element: <Navigate to="home"/>
},
{
path: '/home',
element: <Home/>,
children: [
{
path: '/home',
element: <Navigate to="/home/recommend"/>
},
{
path: '/home/recommend',
element: <HomeRecommend />
},
// ...
]
}
// ...
{
path: '*',
element: <Category />,
}
]
export default routes;
- 使用
useRoutes这个Hook渲染
import { NavLink, useNavigate, useRoutes } from 'react-router-dom';
import routes from './router';
<div className="content">
{ useRoutes(routes) }
</div>
路由懒加载
React中使用路由懒加载,需要使用React.lazy()方法和Suspense组件结合- 定义需要懒加载的路由
const Home = React.lazy(() => import('../pages/Home'))
const HomeRecommend = React.lazy(() => import('../components/HomeRecommend'))
const About = React.lazy(() => import('../pages/About'))
const Login = React.lazy(() => import('../pages/Login'))
- 使用
Suspense组件对App根组件进行包裹,并且添加fallback属性,该属性中的内容是路由组件还没完成加载时所显示的内容
import { Suspense } from 'react';
import { HashRouter } from 'react-router-dom';
<HashRouter>
<Suspense fallback={<h1>Loading</h1>}>
<App />
</Suspense>
</HashRouter>
转载自:https://juejin.cn/post/7265283229505093667