React路由

我睡着的时候不困唉大约 9 分钟前端框架React路由路由编程式导航动态路由路由传参

React 路由

路由介绍

提示

现代的前端应用大多都是 SPA(单页应用程序),也就是只有一个 HTML 页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。为了有效的使用单个页面来管理原来多页面的功能,前端路由应运而生。 前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面)

  • 前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面)
  • 前端路由是一套映射规则,在 React 中,是 URL 路径 与 组件 的对应关系
  • 使用 React 路由简单来说,就是配置 路径和组件(配对)

路由基础

路由基本使用

  • 安装 V6 版本路由
yarn add react-router-dom
  • react-router-dom这个包提供了四个核心的组件
import { HashRouter, Routes, Route, Link } from 'react-router-dom'
  • 使用HashRouterBrowserRouter包裹整个应用,一个项目中只会有一个 Router
  • 使用 Link 指定导航链接
  • 使用Route指定路由规则,在哪里写的 Route,最终匹配到的组件就会渲染到这
import React, { PureComponent } from 'react'
import Home from '../components/Home.js'
import About from '../components/About.js'
import About2 from '../components/About2.js'
import About3 from '../components/About3.js'
import NoMatch from '../components/NoMatch.js'
import { HashRouter, Routes, Route, Link } from 'react-router-dom'

export default class index extends PureComponent {
  render() {
    return (
      <HashRouter>
        <Link to="/">Home</Link>
        {/* 动态路由 */}
        <Link to="/about/123456">About</Link>
        {/* 传参 */}
        <Link to="/about2" state={{ age: '16', name: 'sky' }}>
          About2
        </Link>
        <Link
          to="/about3/123?name=sky&age=16"
          state={{ age: '16', name: 'sky' }}
        >
          About3
        </Link>
        {/* Routes 和 V5版本的路由中的 Switch一样 */}
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about/:userId" element={<About />} />
          <Route path="/about2" element={<About2 />} />
          <Route path="/about3/:id" element={<About3 />} />
          {/* 路由不存在则匹配*号路由 */}
          <Route path="*" element={<NoMatch />} />
        </Routes>
      </HashRouter>
    )
  }
}

注意

  1. 他们都需要包裹在BrowserRouter或者HashRouter里面
  2. Route需要包裹在Routes里面,跟以前的Switch一样
  3. Route里面渲染组件方式是element = {<组件名/>}

路由参数获取

  1. 点击路由基本使用示例代码中的 About3 <Link to='/about3/123?name=sky&age=16' state={{age: '16', name:"sky"}}>About3</Link>
  2. 在 About3 中获取路由传参
import React from 'react'
import { useSearchParams, useParams, useLocation } from 'react-router-dom'
export default function About3() {
  const params = useParams()
  const location = useLocation()
  let [searchparams] = useSearchParams()
  console.log('params', params)
  console.log('location', location)
  console.log('searchParams', searchparams.get('name'))
  console.log('searchParams', searchparams.get('age'))

  return <div>About3</div>
}
  1. 获取参数结论
    • useParams 获取 params 传参,本例中为/123
    • useLocation 获取传入 state,本例为state: {age: '16', name: 'sky'}
    • useSearchParams 获取入 query,本例为?name=sky&age=16

Router 详细说明

  • Router 组件:包裹整个应用,一个 React 应用只需要使用一次
  • 两种常用 Router:HashRouterBrowserRouter
  • HashRouter:使用 URL 的哈希值实现(localhost:3000/#/first
  • BrowserRouter:使用 H5 的 history API 实现(localhost:3000/first

最佳实践

import { HashRouter as Router, Route, Link } from 'react-router-dom'

Link组件最终会渲染成 a 标签,用于指定路由导航

  • to 属性,将来会渲染成 a 标签的 href 属性
  • Link组件无法实现导航的高亮效果

NavLink组件,一个更特殊的Link组件,可以用用于指定当前导航高亮

  • to 属性,用于指定地址,会渲染成 a 标签的 href 属性
  • className,自定义激活时的样式名 可以为字符串或者函数
  • end 匹配子路由时是否高亮
  • caseSensitive 代表匹配路径时是否区分大小写

Route

  • path 的说明
    • 默认情况下,/能够匹配任意/开始的路径
    • 如果 path 的路径匹配上了,那么就可以对应的组件就会被 render
    • 如果 path 没有匹配上,那么会 render null
    • 如果没有指定 path,那么一定会被渲染
  • component 属性变成 element
  • exact 的说明, exact 表示精确匹配某个路径
    • 一般来说,如果路径配置了 /, 都需要配置 exact 属性

Routes 与 404

  • 通常,我们会把Route包裹在一个Routes组件中

  • Routes组件中,不管有多少个路由规则匹配到了,都只会渲染第一个匹配的组件

  • 通过Routes组件非常容易的就能实现 404 错误页面的提示

/* Routes 和 V5版本的路由中的 Switch一样 */
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about/:userId" element={<About />} />
  <Route path="/about2" element={<About2 />} />
  <Route path="/about3/:id" element={<About3 />} />
  {/* 路由不存在则匹配*号路由 */}
  <Route path="*" element={<NoMatch />} />
</Routes>

嵌套路由的配置

嵌套路由一般是配合 Outlet 组件使用,此组件类似于 Vue 的 router-view 组件,告知子路由应该渲染在什么位置

  1. 一级路由配置
import { BrowserRouter, Routes, Route, Navigate, Link } from 'react-router-dom'

import Design from '../view/design/index' // 存在二级路由
import Code from '../view/code/index'
import NotPage from '../view/notPage'
const Nesting = () => {
  return (
    <div>
      {/* HashRouter哈希路由 还是 Browser路由随需求选择 */}
      <BrowserRouter>
        {/* 一级路由 */}
        <Link style={{ marginLeft: '12px' }} to="/code">
          code一级路由
        </Link>
        <Link style={{ marginLeft: '12px' }} to="/design">
          design一级路由
        </Link>
        <Routes>
          {/* 默认初始化入口/code* /}
          <Route path="/" element={<Navigate to="/code" />} />
          {/* Code模块路由 */}
          <Route path="code/*" element={<Code />} />
          {/* Design模块路由 */}
          <Route path="/design/*" element={<Design />} />
          {/* 404页面 */}
          <Route path="*" element={<NotPage />} />
        </Routes>
      </BrowserRouter>
    </div>
  )
}
export default Nesting
  1. 二级路由配置
// view/design/index.js 文件
import { Routes, Route, Link } from 'react-router-dom'
import { Component } from 'react'

export default class Design extends Component {
  render() {
    return (
      <div>
        <header>Design页面</header>
        <Link to="./list">列表-二级路由</Link>
        <Link style={{ marginLeft: '12px' }} to="./addedit">
          新增编辑-二级路由
        </Link>
        <div>
          <Routes>
            <Route path="/list" element={<div>设计列表</div>} />
            <Route path="/addedit" element={<div>设计新增编辑</div>} />
          </Routes>
        </div>
      </div>
    )
  }
}
  • 在 v5 版本中,路由的重定向使用的是 Redirect,在 v6 版本中使用的是 Navigate

编程式导航

  • 场景:点击登录按钮,登录成功后,通过代码跳转到后台首页
  • 编程式导航:通过 JS 代码来实现页面跳转
  1. 使用编程式导航
import React from 'react'
import { Link, useNavigate } from 'react-router-dom'

function RouterNav() {
  const list = [
    { id: '001', title: '消息1' },
    { id: '002', title: '消息2' },
    { id: '003', title: '消息3' }
  ]
  // 编程式导航
  const navigate = useNavigate()

  function replaceShow(id, title) {
    console.log('id, title :>> ', id, title)
    navigate(`/home/message/detail`, { replace: true, state: { id, title } })
  }

  function pushShow(id, title) {
    console.log('object :>> ', id, title)
    navigate(`/home/message/detail`, { replace: false, state: { id, title } })
  }

  return (
    /* <></> 用在react中,其实算是 标签的一个语法糖 表示一个Dom片段 
    它可以在一个内存里面创建一个Dom节点 但是并不在Dom模板上渲染,进而提升性能 */
    <>
      <ul>
        {list.map((i) => (
          <li key={i.id}>
            <Link to={`/home/message/detail/${i.id}/${i.title}`}>
              {i.title}
            </Link>
            <button
              onClick={() => {
                replaceShow(i.id, i.title)
              }}
            >
              replace
            </button>
            <button
              onClick={() => {
                pushShow(i.id, i.title)
              }}
            >
              push
            </button>
          </li>
        ))}
      </ul>
      <hr />
      <button onClick={() => navigate(1)}>前进</button>&nbsp;
      <button onClick={() => navigate(-1)}>后退</button>
    </>
  )
}
export default RouterNav
  1. 渲染到页面
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import RouterNav from './programmedRouteNav/index' // 编程式导航
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <RouterNav />
    </BrowserRouter>
  </React.StrictMode>
)

useRoutes() 路由表

提示

作用:根据路由表,动态创建<Routes><Route>

import React from 'react'
import { NavLink, useRoutes, Navigate } from 'react-router-dom'

const config = [
  {
    path: '/',

    element: <Navigate to="/login" />
  },
  {
    path: '/login',
    element: <div>login</div>
  },
  {
    path: '/code',
    element: <div>code</div>
  },
  {
    path: '*',
    element: <div>404</div>
  }
]

function computedClassName({ isActive }) {
  return isActive ? 'demo' : ''
}

export default function RoutesPage() {
  // useRoutes可以用路由表生成<Routes>...</Routes>结构
  // 根据路由表生成对应的路由规则
  let element = useRoutes(config)
  return (
    <>
      <div>
        {/* 路由链接 */}
        <NavLink className={computedClassName} to="/login">
          login
        </NavLink>
        <NavLink className={computedClassName} to="/code">
          code
        </NavLink>
      </div>

      {/* 注册路由 */}
      {element}
    </>
  )
}

V6 与 V5 版本的比较

  1. Switch 标签替换成 Routes

  2. 原本渲染组件的方式由<Route path="/" component={Home}>变为<Route path="/" element={<Home/>}(其中 Home 是对应的组件)

  3. 编程式导航由useHistory替换成useNavigate

  4. 重定向由Redirect替换成Navigate

    • V5 版本写法
    import { Redirect } from 'react-router-dom'
    ;<Redirect to="/home" />
    
    • V6 版本写法
    import { Navigate } from 'react-router-dom'
    ;<Route path="/" element={<Navigate to="/about" />} />
    
  5. 新增useRoutes来替代版本 5 需要安装react-router-config插件才能实现的功能

  6. NavLink 选中高亮

    • 在 v5 版本中,实现 NavLink 高亮使用的是 NavLink 组件标签中的 activeClassName 属性,当你点击 NavLink 标签时,加哪个样式的类名
    <NavLink activeClassName='demo' className="list-group-item" to="/about">About</NavLink>
    <NavLink activeClassName='demo' className="list-group-item" to="/home">Home</NavLink>
    
    • 在 v6 版本中,想要实现自定义的类名,需要把 className 的值写成一个函数

      import React, { PureComponent } from 'react'
      import Home from '../components/Home.js'
      import About from '../components/About.js'
      import { HashRouter, Routes, Route, NavLink } from 'react-router-dom'
      import './index.css'
      // 抽离激活样式
      function computedClassName({ isActive }) {
        return isActive ? 'demo' : ''
      }
      export default class index extends PureComponent {
        render() {
          return (
            <HashRouter>
              <NavLink to="/" className={computedClassName}>
                Home
              </NavLink>
              {/* 默认情况下,当 About 的子组件匹配成功,About 的导航也会高亮,
              当 NavLink 上添加了 end 属性后,若 About 的子组件匹配成功,则 About 的导航没有高亮效果 */}
              <NavLink to="/about/123456" className={computedClassName} end>
                About
              </NavLink>
      
              {/* Routes 和 V5版本的路由中的 Switch一样 */}
              <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/about/:userId" element={<About />} />
              </Routes>
            </HashRouter>
          )
        }
      }
      

路由懒加载

import { createElement, lazy, Suspense } from 'react'
import { useRoutes, NavLink, Navigate } from 'react-router-dom'
import Layout from './index'

const Loading = () => {
  return (
    <div
      style={{
        background: 'rgba(0,0,0,0.3)',
        color: '#ffffff',
        height: '100vh',
        width: '100vh',
        overflow: 'hidden',
        lineHeight: '50vh',
        position: 'fixed',
        top: 0,
        left: 0,
        fontSize: '60px',
        textAlign: 'center'
      }}
    >
      加载中...
    </div>
  )
}

// lazy实现路由懒加载
// Suspense 网络异常/网络状态不好的时候加载中
const lazyLoad = (src) => (
  <Suspense fallback={<Loading />}>{createElement(lazy(src))}</Suspense>
)
const config = [
  {
    path: '/',
    element: <Navigate to="/layout" /> // 重定向
  },
  {
    path: '/layout',
    element: <Layout /> // 基础布局不需要懒加载
  },
  {
    path: '/root',
    element: lazyLoad(() => import('../view/root'))
  },
  {
    path: '/code',
    element: lazyLoad(() => import('../view/code/index'))
  }
]

export default function RenderLazy() {
  // useRoutes可以用路由表生成<Routes>...</Routes>结构
  // 根据路由表生成对应的路由规则
  const element = useRoutes(config)

  return (
    <div>
      <div>
        {/* 路由链接 */}
        <NavLink to="/">Layout</NavLink>
        <NavLink to="/root">root</NavLink>
        <NavLink to="/code">code</NavLink>
      </div>
      {/* 注册路由 */}
      {element}
    </div>
  )
}