- 2021-11-3: reactRouter@6 发布
- 说明:V4和V5的版本区别不是很大, 5和6 的区别很多,迁移要注意!
1. react-router 是什么?有什么特点?
react-router是一个强大的路由库,建立在react的基础之上,可以实现单页应用(不需要刷新页面),使url和网页上的数据保持同步。
单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML页面并在用户与应用程序交互时【动态更新】该页面的Web应用程序。
- 可实现页面局部刷新,而不需要刷新整个页面
- URL和页面同步
- 可以实现嵌套路由,动态路由,分组路由,以及代码分割等功能,使前端路由更加灵活易用。
2. react-router有几种路由模式?
<BrowserRouter/>:浏览器模式,基于HTML5的historyAPI实现<HashRouter/>: 有#号,(刷新网页能找到对应路径,但会丢失状态)<MemoryRouter/>:路由保存在内存中,不能前进后退(因为地址栏没变化)<NativRouter/>:移动端使用,配合ReactNative原生使用<StaticRouter/>: 静态路由,需要如后台服务器配合设置
3. react-router的history是如何创建的?
React-Router会自动帮我们创建history,由路由器组件如<BrowserRouter/>创建。
React-Router中history是基于HTML5的history API。也就是对window.history这个对象进行封装。
- history对象作用:监听浏览器地址栏的变化,解析URL转化成location,然后router匹配到路由,最后渲染出相应的组件。
history是一个管理js应用session会话历史的js库。它将不同环境的变量统一成了一个简易的API来管理堆栈、导航、确认跳转及session间的持续状态
4. react-router 如何实现路由跳转?
import { Navigate } from 'react-router-dom':路由重定向(V6)<NavLink/><Link/>import { Redirect } from 'react-router-dom': 路由重定向(V5)
编程式导航:
- V5:
this.props.history.push("/home") - V6: (函数组件)
useNavigate(); navigate('/course');
声明式导航:
- V5:提供了activeClassName类名实现高亮
<NavLink activeClassName="active" to="/home"> 首页</NavLink><Link to="/course">课程</Link>
- V6 会自动给匹配到的路由添加active类名,用于实现高亮效果
<NavLink to="/home">首页</NavLink><Link to="/course">课程</Link>
5. 高阶组件withRouter是什么?
作用:默认情况下必须是【经过路由匹配渲染】的组件才存在this.props,才有路由参数,才能使用编程式导航的写法。执行this.props.history.push。然而不是所有的组件都直接与路由相连,当组件需要路由参数的时候,使用withRouter就可以给此组件传入路由参数。
V5 中如果想要在props上获取路由属性:
1.需要在render里手动传入!
2.使用提供的高阶组件withRouter
<Route
path="/home"
render = {
(props) => {
return this.hasToken() ? <Home {...props} /> : <NotFound/>
}
}
/>
V6 废弃了 react-router-dom提供的高阶组件 withRouter!,需要自己封装
import { useLocation, useNavigate, useParams } from "react-router-dom"
export default function withRouter(Child) {
return (props) => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Child
location={location}
navigate={navigate}
params={params}
{...props}
/>
)
}
}
class EgList extends Component {
// ... 省略代码
}
export default withRouter(EgList);
6. react-router 如何实现路由传参?
- params
- search
- 隐式传参 state
6.1 params参数
6.1.1 传值:注册路由的时候需要声明
<NavLink to="/list/127">列表 params</NavLink>
<Route path="/list/:from/" element={<EgList />} />
V6 注册路由时:声明params参数
6.1.2 取值方式:
- V5:
this.props.match.params获取
- V6:
- 函数组件直接使用
useParams()获取 - 类组件中需要自己封装
withRouter高阶组件包裹类组件,然后通过this.props.params.id方式获取
- 函数组件直接使用
6.2 search 链接?后的参数:
6.2.1 传值:直接拼接
<NavLink to="/home?id=12">首页 query参数</NavLink>
6.2.2 取值方式:
- V5
this.props.location.search.split('=')\[1]
- V6:
- 函数组件:使用
useSearchParams().get("id") - 类组件:使用封装过的withRouter,
this.props.location.search
- 函数组件:使用
6.3 state 参数:
6.3.1 传值:
- 声明式传值:
<Link to={path} state={state}>首页</Link> - 编程式导航传值:
- V5:
this.props.history.push('/home',{state:{...state}}) - 函数组件(V6):
congst navigate = useNavigate(); navigate("/home", {replace:false, state:{...state}}) - V5:
6.3.2 取值方式:
- V5:
this.props.location.state
- V6:
- 函数组件:使用
useLocation() - 类组件:高阶组件withRouter,
this.props.location.state.xx
- 函数组件:使用
const {state:{name,sex,school}} = useLocation();
7. 什么是动态路由?如何实现?
定义: 路由中带有参数的路由 如 detail/:id
// V6版本:路由表里声明
<Routes>
<Route path="detail/:id" element={<Detail />} />
<Routes>
8. <Link>和<a>的区别
<Link>的底层是对a的封装
- 阻止a的默认事件
- 有onclick 就执行onclick
- 底层使用的是history跳转,所以不会刷新页面
9. react-router push 和replace的区别?
- push 是添加:可回到上级
- replace 是替换: 不能回到上级
10.如何实现默认导航?
- V5版本:
Redireact - V6版本:
自定义实现Redireact使用Navigate
下面是V6版本的重定向方式,方式一也是V5版本的写法(V6中已移除):
import { Route, Routes, Navigate } from 'react-router-dom';
import Redirect from './redirect';
{/* 路由重定向 方式1 */}
<Route path="/" element={<Redirect to='/home/' />} />
{/* 路由重定向 方式2 */}
<Route path="/redirect" element={<Navigate to='/home/' />} />
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
export default function Redirect(props){
const navigate = useNavigate();
useEffect(()=>{
navigate(props.to, {replace:true})
})
return null;
}
11. useRoutes是干什么用的?
声明路由配置
import { useRoutes } from 'react-router-dom';
import Redirect from './redirect';
// 路由表
// 方式一:使用hook方式, useRoutes 声明式路由配置方法
const routes1 =[
{
path: "/home",
element: <App />,
children: [{
path: "eg1",
element: <LifecycleTest />,
}, {
path: "eg2",
element: <ChildrenGroup />,
}]
}, {
path: "detail/:id",
element: <Detail />
}{
path: "/",
element: <Redirect to='/home/' />,
}
];
export default function SetRoutes(){
return useRoutes(routes1);
}
import SetRoutes from './router/router1'; // 引入路由配置表
root.render(
<React.StrictMode>
{/* 加入路由 */}
<HashRouter>
<SetRoutes/>
</HashRouter>
</React.StrictMode>
);
12. 如何实现404页面:
<Route path="*" element={</NotFound>}>
13. 怎么实现路由懒加载:
import React, { lazy, Suspense } from 'react'
export default function LazyLoad(path) {
const Element = lazy(() => import(`./${path}`))
return (
<Suspense>
<Element />
</Suspense>
)
}
import { useRoutes } from 'react-router-dom'
import LazyLoad from '../LazyLoad'
export default function MRoute() {
const element = useRoutes([
{
path: '/course',
element: LazyLoad('Course'),
children: [
{
path: 'js',
element: LazyLoad('JsCourse'),
},
// 省略......
],
}
])
return element
}
14. react-router中hooks语法:
注意: 你需要 React >= 16.8 才能使用这些hook,因为16.8 才增加了hook语法
- useLocation()
- useSearchParams()
- useParams()
- useNavigate()
- useRoutes()
- useMatch()
- useResolvedPath()
- useNavigateType()
- useInRouterContext()
- useOutlet()
react-router 常用 hooks 表:
| hooks名 | 作用 | 说明 |
|---|---|---|
| useParams | 返回当前参数 | 根据路径读取参数 |
| useSearchParams | 用来匹配URL中?后面的搜索参数 | |
| useNavigate | 返回当前路由 | 代替原有V5中的 useHistory |
| useOutlet | 返回根据路由生成的element | |
| useLocation | 返回当前的location 对象 | 对标V5中的 location属性 |
| useRoutes | 同Routers组件一样,只不过是在js中使用 | |
| useMatch | 返回当前匹配信息,对标 5 路由组件的 match 属性 | 需要传入具体的url,react会帮你分析这个路径并返回 |
| useResolvedPath | 给定一个URL值,解析其中的path、search、hash值 | 可以输入任意路径地址,不是当前系统的也可以 |
| useNavigateType | 返回当前的导航类型(用户是如何来到当前页面的) | 返回值:POP(刷新页面)、PUSH、REPLACE |
| useInRouterContext | 如果组件在Router的上下文中呈现,则useInRouterContext返回true,否则返回false |
V5中其实已经新增了 [
useHistory][useLocation][useParams][useRouteMatch]这几个hooks,但V6是使用react hooks重建的
15. V5 和V6 的区别:
- 包的大小,V6更小
- 废弃:
- V6 废弃
<Switch>,改用<Routes>包裹<Route>路由表 - V6 废弃exact
- V6 移除activeClassName,会
自动add active类名 - V6 使用
element引入组件,替换V5使用的component和render - V6
useHistory()被移除, 用useNavigate()实现编程式导航 - V6
没有Redirect组件,需要自定义,或者使用Navigate组件 - 使用
useRoutes()替代V5的router.config.js - 废弃了
withRouter,需要自定义
- V6 废弃
- 嵌套路由更容易,且使用相对路径
- 使用Outlet组件,此组件是一个占位符,告诉 React Router 嵌套的内容应该放到哪里
- 使用index 指定默认路由, 或者path为空(声明式路由中,不能写index)
- v6 中,Route 先后顺序不再重要,React Router 能够自动找出最优匹配路径
- V6 中函数组件使用
useParams()和useSearchParams()获取路由参数 - 原理:
- V5:本质在于 Route 组件,当路由上下文 context 改变的时候,Route 组件重新渲染,然后通过匹配来确定业务组件是否渲染。
- V6: 本质在于 Routes 组件,当 location 上下文改变的时候,Routes 重新渲染,重新形成渲染分支,然后通过 provider 方式逐层传递 Outlet,进行匹配渲染。