最快速最直白带你上手 React-Router6

1,569 阅读7分钟

最快速最直白带你上手 React-Router6

开源

个人开源的leno-admin后台管理项目,前端技术栈:reactHooksant-design;后端技术栈:koamysqlredis,整个项目包含web端electron客户端mob移动端template基础模板,能够满足你快速开发一整套后台管理项目;如果你觉得不错,就为作者点个✨star✨吧,你的支持就是对我最大的鼓励;

演示地址

文档地址

源码github地址

一、React Router 种类

  • 1、react-router 该包是核心库;
  • 2、react-router-dom 该包是专门用来开发web网页端的路由包;
  • 3、react-router-native 该报是专门用来开发移动端的,里面专门添加了ReactNative的API;

本文主要是讲解react-router-dom的使用;

二、安装

首先是进行npm包的安装,React-Router 只需要安装一个包即可使用所有的路由API,还是比较方便的;

npm i react-router-dom
复制代码

三、BrowserRouter与HashRouter

HashRouter 直观感受就是网页路径带 #;

BrowserRouter 直观感受就是网页路径不带 #,项目开发中主要使用BrowserRouter

index.tsx入口文件内从react-router-dom中按需引入BrowserRouter对入口组件进行包裹,代码如下:🥗

import React from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import { ConfigProvider } from 'antd'
import App from '@/App'

const root = document.getElementById('root')
if (root) {
  createRoot(root).render(
    <BrowserRouter>
      <ConfigProvider>
        <App />
      </ConfigProvider>
    </BrowserRouter>,
  )
}
复制代码

四、路由链接与注册路由

react-router里面的路由链接分为声明式导航NavLink,Link与编程式导航useNavigate,注册路由则为RoutesRouteOutlet;

4-1 声明式导航(NavLink、Link) (即路由跳转功能)

  • NavLink是带上有选中样式功能配置的
// App.tsx
      <NavLink className={currentHighLightFn} to="/home">
        GoHome
      </NavLink>
      <NavLink className={currentHighLightFn} end to="/about/news">
        go about
      </NavLink>
复制代码

to为跳转路径,后面跟路径地址,可以写/home也可以直接写home;当然如果是多级路由可写/about/news,如果是嵌套路由的话,子路可以直接写news;
end属性,如果Navink添加了end属性后,若Home的子组件匹配成功,那Home本身的高亮效果就会没有,也就是说,没添加end的话,则父组件和子组件都有高亮效果,添加end的话,则子组件有高亮效果的话,父组件身上的就会消失;

  • Link
<Link to={`detail/${item.id}/${item.content}`}>{item.content}</Link>

// 用法基本与NavLink相似,但是没有高亮效果;
复制代码

4-1-1 选中变色

其实选中变色就是react-router提供一个变量isActive,如果isActive为true,就表示选中当前路由按钮,那就给他的className添加变色的类名即可,直接上封装的函数currentHighLightFn:

// utils/index.ts
import { IIsActive } from '@/type'
/* 当前高亮函数 */
export function currentHighLightFn(obj: IIsActive): string {
  const { isActive } = obj
  return isActive ? 'sidebar current-highlight' : 'sidebar'
}

//type/index.d.ts
export type IIsActive = {
  isActive: boolean
}
复制代码

4-2 编程式导航 useNavigate

一般大多数时候声明式导航都无法满足我们的功能需求,其实在项目开发中我们更多使用的都是编程式导航,一般都是对方是否满足我们设置的条件才允许对方跳转,否则不允许对方跳转,直接上代码:

// views/login/index.tsx
import { useNavigate } from 'react-router-dom'

export default function Login() {
  let navigate = useNavigate() // 调用useNavigate函数,返回函数,一般命名navigate
  
  const onFinish = async ({ username, password }: ILogin) => {
    const { status, token } = (await loginAPI(username, password)) as any
    if (status === 200) {
      localStorage.setItem('token_01', token)
      navigate('/home') 
      message.success('登录成功!')
    }
  }
  // 下面简写了,这是一个登录功能,但是全部代码太长了,所以下面就简单的布置一个按钮
  return (
     <div>
         <button onClikc='onFinish'>点击登录</button> 
     </div>
     )
 }
复制代码
navigate(第一个参数为跳转路径[如果是子路由则不要 \ ],第二个为一个对象{
 replace:false(默认为false,表示是否替换路径,覆盖from的路径),
 state:{
    id:11
  }(传输数据,state属性名不可以变)
  })
复制代码

额外补充点:如果navigate传-1或1就是前进一层路由或者后退一层路由;

4-3 注册路由基础版(Routes、Route)

Route必须需要被Routes包裹,如果直接单写Route则会报错无法运行,Route有两个关键的属性,一个是path路径,一个则是element绑定的组件,直接上代码案例:

      {/* 注册路由 */}
      <Routes>
        <Route path="/about" element={<About />} />
        <Route caseSensitive path="/home" element={<Home />} />
        <Route path="/" element={<Navigate to="/about" />} /> // 重定向
      </Routes>
复制代码

Navigate路径重定向,后面需要跟to=路径,一般设置根路径/的跳转页面地址;
Route 中的 caseSensitive 属性:匹配的时候是否区分大小写(默认值为false)

4-4 注册路由表useRoutes,工程化使用React-router

虽然RoutesRoute使用起来非常方便简单,但是在项目开发中,经常使用的都是路由表useRoutes,也即是模块化路由管理,对未来项目路由的管理及扩展都是非常友好的,直接上代码:

// App.tsx入口文件
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import '@/style/App.scss'
/* 引入路由表 */
import routes from '@/routes'
/* 引入工具函数 */
import { currentHighLightFn } from '@/utils'
import './style/App.css'

export default function App() {
  /* 路由表 */
  const element = useRoutes(routes)

  return (
    <div>
      {/* 路由连接 */}
      <NavLink className={currentHighLightFn} to="home">
        go home
      </NavLink>
      <NavLink className={currentHighLightFn} end to="/about">
        go about
      </NavLink>
      -----------------------------
      {/* 注册路由 */}
      {element}
    </div>
  )
}
复制代码

路由文件目录格式:

分路由模块about.tsx代码格式:

import React from 'react'
/* 路由页面 */
import About from '@/views/About'
import News from '@/views/News'
import Message from '@/views/Message/indx'
import Detail from '@/views/Detail'

export default {
  path: '/about',
  element: <About />,
  children: [
    {
      path: 'news',
      element: <News />,
    },
    {
      path: 'message',
      element: <Message />,
      children: [
        {
          path: 'detail/:id/:content',
          element: <Detail />,
        },
      ],
    },
  ],
}
复制代码

路由入口文件index.tsx代码如下:

import React from 'react'
import { Navigate } from 'react-router-dom'
/* 路由页面 */
import Home from '@/views/Home'
/* 子路由 */
import aboutRoute from './modules/about'

const commentRoutes = [aboutRoute]

export default [
  {
    path: '/home',
    element: <Home />,
  },
  ...commentRoutes,
  {
    path: '/',
    element: <Navigate to="/home" />,
  },
]
复制代码

4-5 指定路由呈现位置Outlet

对于子路由呈现位置,需要使用Outlet在指定位置进行呈现展位,代码如下:

import React from 'react'
import { NavLink, Outlet } from 'react-router-dom'
/* 工具函数 */
import { currentHighLightFn } from '@/utils'
/* ant */
import { Button } from 'antd'

export default function About() {
  return (
    <div>
      <h1>About</h1>
      {/* 路由连接 */}
      <NavLink className={currentHighLightFn} to="news">
        News
      </NavLink>
      <NavLink className={currentHighLightFn} to="message">
        Message
      </NavLink>
      <Button type="primary" value="large">
        Large
      </Button>
      {/* 注册路由 */}
      <Outlet />
    </div>
  )
}
复制代码

五、路由传值

5-1 路由params参数的使用

第一步 在父组件Message声明数据

import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'

export default function Message() {
  const [message] = useState([
    { id: 0, content: '我是第一个' },
    { id: 1, content: '我是第二个' },
    { id: 2, content: '我是第三个' },
  ])
  return (
    <div>
      <h2>Message 我是二级路由</h2>
      {message.map((item) => {
        return (
          <li key={item.id}>
            <Link to={`detail/${item.id}/${item.content}`}>{item.content}</Link>
            // 数据通过路径传到子路由,需按照上面的格式传值;
          </li>
        )
      })}
      --------------- 三级路由 ---------------------
      <Outlet />
    </div>
  )
}
复制代码

第二步 路由表内值路径占位

import React from 'react'
/* 路由页面 */
import About from '@/views/About'
import News from '@/views/News'
import Message from '@/views/Message/indx'
import Detail from '@/views/Detail'

export default {
  path: '/about',
  element: <About />,
  children: [
    {
      path: 'news',
      element: <News />,
    },
    {
      path: 'message',
      element: <Message />,
      children: [
        {
          path: 'detail/:id/:content', // 此处对要传输的数据进行占位
          element: <Detail />,
        },
      ],
    },
  ],
}
复制代码

第三步 子组件内通过useParams进行取值

import React from 'react'
import { useParams } from 'react-router-dom'

export default function Detail() {
  const { id, content } = useParams()

  return (
    <div>
      <h3>{id}</h3>
      <h3>{content}</h3>
    </div>
  )
}
复制代码

通过简单的三步,便可以完成params传值,还不快去试试呀!

5-2 路由的search参数(类似于Vue的query参数)

第一步 在父组件Message声明数据

      {message.map((item) => {
        return (
          <li key={item.id}>
            <Link to={`detail?id=${item.id}&content=${item.content}`}>{item.content}</Link>
            // 数据通过路径传到子路由,需按照上面的格式传值;
          </li>
        )
      })}
复制代码

第二步 路由表内不用占位

 {
      path: 'message',
      element: <Message />,
      children: [
        {
          path: 'detail',
          element: <Detail />,
        },
      ],
    },
复制代码

第三步 子组件内通过useSearchParams取值

import React from 'react'
import { useSearchParams } from 'react-router-dom'

export default function Detail() {
  const [search,setSearch] = useSearchParams()
  const id = search.get('id')
  const content = search.get('content')

  return (
    <div>
      <h3>{id}</h3>
      <h3>{content}</h3>
    </div>
  )
}
复制代码

额外补充点,setSearch对当前子组件的search参数进行更改

// 你可以写一个按钮,点击改变该子组件内部的search参数

<button onClick={()=>setSearch('id=111&content=我改变啦')}>点我更新页面search参数</button>
复制代码

额外补充点,useLocation获取路由参数信息

import {useLocation} from 'react-router-dom'

const data = useLocation()
复制代码

获取的路由信息图片:

5-3 路由的state参数

第一步 在父组件Message声明数据

      {message.map((item) => {
        return (
          <li key={item.id}>
            <Link to="detail" state={{
                id:item.id,
                content:item.content
            }}>{item.content}</Link>
            // 数据通过路径传到子路由,需按照上面的格式传值;
          </li>
        )
      })}
复制代码

第二步 路由表内不用占位

 {
      path: 'message',
      element: <Message />,
      children: [
        {
          path: 'detail',
          element: <Detail />,
        },
      ],
    },
复制代码

第三步 子组件内通过useLocation取值

import React from 'react'
import { useLocation } from 'react-router-dom'

export default function Detail() {
  const {state:{id,content} = useLocation()

  return (
    <div>
      <h3>{id}</h3>
      <h3>{content}</h3>
    </div>
  )
}
复制代码

六、额外知识点

6-1 useInRouterContext

判断你现在的组件是否处于被BrowserRouterHashRouter路由包裹,如果被包裹了调用useInRouterContext()会返回布尔值true,没有被包裹住的话,返回的就是false

6-2 useNavigationType

useNavigationType的作用是得知用户是如何进来当前页面的;
返回值:POP,PUSH,REPLACE; POP: 重新刷新当前页面打开了这个路由组件;

6-3 useOutlet

useOutlet检测嵌套路由是否挂载,如果已经挂载了,返回值回事嵌套的路由对象,如果没有挂载,则返回值为null

6-4 useResolvedPath

帮你解析路由路径:

console.log(useResolvedPath('/home/?id=111&name=zzz'))

=> 浏览器打印:{pathname:'/home',search:'?id=111&name=zzz'}