ReactRouter 面试题

1,340 阅读7分钟
  • 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有几种路由模式?

  1. <BrowserRouter/>:浏览器模式,基于HTML5的historyAPI实现
  2. <HashRouter/>: 有#号,(刷新网页能找到对应路径,但会丢失状态)
  3. <MemoryRouter/>:路由保存在内存中,不能前进后退(因为地址栏没变化)
  4. <NativRouter/>:移动端使用,配合ReactNative原生使用
  5. <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);

image.png

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}})
    

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语法

  1. useLocation()
  2. useSearchParams()
  3. useParams()
  4. useNavigate()
  5. useRoutes()
  6. useMatch()
  7. useResolvedPath()
  8. useNavigateType()
  9. useInRouterContext()
  10. 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(刷新页面)、PUSHREPLACE
useInRouterContext如果组件在Router的上下文中呈现,则useInRouterContext返回true,否则返回false

V5中其实已经新增了 [useHistory][useLocation][useParams][useRouteMatch]这几个hooks,但V6是使用react hooks重建的

15. V5 和V6 的区别:

  1. 包的大小,V6更小
  2. 废弃:
    1. V6 废弃<Switch>,改用<Routes> 包裹<Route>路由表
    2. V6 废弃exact
    3. V6 移除activeClassName,会自动add active类名
    4. V6 使用element引入组件,替换V5使用的component和render
    5. V6 useHistory()被移除, 用useNavigate()实现编程式导航
    6. V6 没有Redirect组件,需要自定义,或者使用Navigate组件
    7. 使用useRoutes()替代V5的router.config.js
    8. 废弃了withRouter,需要自定义
  3. 嵌套路由更容易,且使用相对路径
  4. 使用Outlet组件,此组件是一个占位符,告诉 React Router 嵌套的内容应该放到哪里
  5. 使用index 指定默认路由, 或者path为空(声明式路由中,不能写index)
  6. v6 中,Route 先后顺序不再重要,React Router 能够自动找出最优匹配路径
  7. V6 中函数组件使用useParams()和useSearchParams()获取路由参数
  8. 原理:
    • V5:本质在于 Route 组件,当路由上下文 context 改变的时候,Route 组件重新渲染,然后通过匹配来确定业务组件是否渲染。
    • V6: 本质在于 Routes 组件,当 location 上下文改变的时候,Routes 重新渲染,重新形成渲染分支,然后通过 provider 方式逐层传递 Outlet,进行匹配渲染。