React-Router

前端路由的核心:改变URL,但是页面不进行整体的刷新。官网移步Home v6.21.3 | React Router

一、 Router的基本使用

React Router的版本4开始,路由不再集中在一个包中进行管理了:

  • react-router是router的核心部分代码;
  • react-router-dom是用于浏览器的;
  • react-router-native是用于原生应用的;

安装react-router-dom会自动帮助我们安装react-router的依赖;

1.1 安装

shell 复制代码
npm install react-router-dom

路由的核心是映射关系,路径对应组件

1.2 BrowserRouter和HashRouter/Routers和route

  • BrowserRouter使用history模式;
  • HashRouter使用hash模式;像Vue中的hash一样路径中出现/#
  • <Routes> 都会匹配当前位置的一组子路由。
  • Route用于路径的匹配;
    • path:匹配路径
    • element 路由与 URL 匹配时要渲染的 React 元素
    • 其他属性参考官网 入口文件
js 复制代码
import React, { StrictMode } from "react"
import ReactDOM from 'react-dom/client';
import { HashRouter } from 'react-router-dom'
import App from './App.jsx'
const root = ReactDOM.createRoot(document.getElementById("root"))

// HashRouter可以更换成普通的BrowserRouter模式

root.render(
  <StrictMode>
    <HashRouter>
      <App />
    </HashRouter>
  </StrictMode>
)

App.jsx

jsx 复制代码
import React, { PureComponent } from 'react'
import { Route, Routes } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'
export class App extends PureComponent {
  render() {
    return (
      <div className='app'>
        <header className='Header'>
          顶部导航栏
        </header>
        <div className='main'> 
          主体部分
          <hr />
          展示路由
          <div style={{border:"1px solid red",height:"200px"}}>
            <Routes>
              <Route path='/home'  element={<Home/>}></Route>
              <Route path='/about'  element={<About/>}></Route>
              <Route path='/user'  element={<User/>}></Route>
            </Routes>
          </div>
        </div>
        <footer className='footer'>
          页脚  
        </footer>
      </div>
    )
  }
}

export default App

到这里已经实现了效果,但是每次都需要手动在地址栏去修改路由地址才能渲染对应的组件,那有没有一种办法可以进行界面点击跳转呢?当然可以添加了

1.3Link和NavLink:

  • 通常路径的跳转是使用Link组件,最终会被渲染成a元素;
  • NavLink是在Link基础之上增加了一些样式属性;
  • to属性:Link中最重要的属性,用于设置跳转到的路径;

Link使用

App.jsx

jsx 复制代码
import { Link, Route, Routes } from 'react-router-dom'
//other import
export class App extends PureComponent {
  render() {
    return (
      <div className='app'>
        <header className='Header'>
          顶部导航栏
          <h1>
            <Link to="/home">
              首页
            </Link>
          </h1>
          <h1>
            <Link to="/about">
              关于
            </Link>
          </h1>
          <h1>
            <Link to="/user">
              我的
            </Link>
          </h1>
        </header>
         {/* other code */}
      </div>
    )
  }
}

export default App

虽然点击可以切换但是如何高亮显示我们选中的是哪一个呢?这时候就需要用到另外一个组件!!!

这里点击激活路由就让a标签显示为绿色的我来修改一下:将<Link>修改为<NavLink>

默认匹配成功时,NavLink就会添加上一个动态的active class;所以直接编写样式

css 复制代码
.active{
  color: green
}

当然了如果你不想使用导入css文件的话也可以使用函数来修改:作为了解

这里是动态添加样式,也可以动态添加class进行修改,搭配样式选择器来修改样式

jsx 复制代码
//...
 <h3>
    <NavLink to="/home" style={({isActive})=>({color:isActive?"black":""})}> 首页</NavLink>
    <NavLink to="/about" style={({isActive})=>({color:isActive?"black":""})}> 关于</NavLink>
    <NavLink to="/user" style={({isActive})=>({color:isActive?"black":""})}> 我的</NavLink>
 </h3>
 //...

如果我们在路径中只输入了一个根路径界面就匹配不到对应路由进行渲染展示,因此我这里新增一个/作为路径搭配Navigate导航来进行跳转 借用上文示例 App.jsx

jsx 复制代码
<div style={{ border: "1px solid red", height: "200px" }}>
    <Routes>
       //这里使用方式
      <Route path='/' element={<Navigate to="/home"/>}></Route>
      <Route path='/home' element={<Home />}></Route>
      <Route path='/about' element={<About />}></Route>
      <Route path='/user' element={<User />}></Route>
      {/* 这里使用通配符,如果用户子地址栏输入一个不存在的路由地址那么就进行匹配并且渲染element元素进行展示,当然你也可以进行抽离 */}
       <Route path='*' element={<div>页面丢失了</div>}></Route>
    </Routes>
</div>

二、Router的路由嵌套

学习过Vue的都知道,vue-router存在路由嵌套关系。那我们来看一下ReactRouter是怎样实现路由嵌套的

新建两个页面src/pages/HomeSon1.jsx和src/pages/HomeSon2.jsx

修改入口文件App.jsx

jsx 复制代码
import React, { PureComponent } from 'react'
import { NavLink, Navigate, Route, Routes } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'

import HomeSon1 from './pages/HomeSon1'
import HomeSon2 from './pages/HomeSon2'

import "./style.css"
export class App extends PureComponent {
  render() {
    return (
      <div className='app'>
        <header className='Header'>
          顶部导航栏

          <h3>
            <NavLink to="/home" > 首页</NavLink>
            <NavLink to="/about" > 关于</NavLink>
            <NavLink to="/user" > 我的</NavLink>
          </h3>

        </header>
        <div className='main'>
          主体部分
          <hr />
          展示路由
          <div style={{ border: "1px solid red", height: "200px" }}>
            <Routes>
              <Route path='/' element={<Navigate to="/home"/>}></Route>
              <Route path='/home' element={<Home />}>
                <Route path='/home/HomeSon1' element={<HomeSon1/>}></Route>
                <Route path='/home/HomeSon2' element={<HomeSon2/>}></Route>
              </Route>
              <Route path='/about' element={<About />}></Route>
              <Route path='/user' element={<User />}></Route>
              {/* 这里使用通配符,如果用户子地址栏输入一个不存在的路由地址那么就进行匹配并且渲染element元素进行展示,当然你也可以进行抽离 */}
              <Route path='*' element={<div>页面丢失了</div>}></Route>
            </Routes>
          </div>
        </div>
        <footer className='footer'>
          页脚
        </footer>

      </div>
    )
  }
}

export default App

修改src/Home.jsx

jsx 复制代码
import React, { PureComponent } from 'react'
import { Outlet,NavLink } from 'react-router-dom'
export class Home extends PureComponent {
  render() {
    return (
      <div>
        首页页面
        <div>
          导航:
          <NavLink to="/home/HomeSon1" > 首页嵌套1</NavLink>------
          <NavLink to="/home/HomeSon2" > 首页嵌套2</NavLink>
        </div>
      </div>
    )
  }
}
export default Home

这时候点击会发现页面中并没有出现我们想要的样子内容。 因此我们需要使用<Outlet>

父路由元素中应使用 <Outlet> 来呈现其子路由元素。这样就可以在呈现子路由时显示嵌套用户界面。如果父路由完全匹配,则会呈现子索引路由;如果没有索引路由,则不会呈现任何内容

jsx 复制代码
//其实本质就是作为站位元素
//....
export class Home extends PureComponent {
  render() {
    return (
      <div>
        首页页面
        ...
        <div>
          展示子路由
          <Outlet/>
        </div>
      </div>
    )
  }
}

//...

此时访问路径会发现还有一个小问题就是我们访问/home路径时候界面并没有出现子路由界面,因此我们要进行优化使用Navigate App.jsx

jsx 复制代码
<Route path='/home' element={<Home />}>
    <Route path='/home' element={<Navigate to="/home/HomeSon1"/>}></Route>
    <Route path='/home/HomeSon1' element={<HomeSon1/>}></Route>
    <Route path='/home/HomeSon2' element={<HomeSon2/>}></Route>
</Route>

三、Router的代码跳转

目前我们实现的跳转主要是通过Link或者NavLink进行跳转的,实际上我们也可以通过JavaScript代码进行跳转 这里我们要用到Hooks中的useNavigate

但是Hooks只能在函数式组件中使用,所以我们来改造一下App.jsx

jsx 复制代码
import {  Navigate, Route, Routes, useNavigate } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'

import HomeSon1 from './pages/HomeSon1'
import HomeSon2 from './pages/HomeSon2'

export function App() {
  const navigate = useNavigate()

  return (
    <div className='app'>
      <header className='Header'>
        顶部导航栏
        <h3>
          <button onClick={e=>navigate("/home")}>首页</button>
          <button onClick={e=>navigate("/about")}>关于</button>
          <button onClick={e=>navigate("/user")}>我的</button>
        </h3>
      </header>
      <div className='main'>
        主体部分
        <hr />
        展示路由
        <div style={{ border: "1px solid red", height: "200px" }}>
          <Routes>
            <Route path='/' element={<Navigate to="/home" />}></Route>
            <Route path='/home' element={<Home />}>
              <Route path='/home' element={<Navigate to="/home/HomeSon1" />}></Route>
              <Route path='/home/HomeSon1' element={<HomeSon1 />}></Route>
              <Route path='/home/HomeSon2' element={<HomeSon2 />}></Route>
            </Route>
            <Route path='/about' element={<About />}></Route>
            <Route path='/user' element={<User />}></Route>
            {/* 这里使用通配符,如果用户子地址栏输入一个不存在的路由地址那么就进行匹配并且渲染element元素进行展示,当然你也可以进行抽离 */}
            <Route path='*' element={<div>页面丢失了</div>}></Route>
          </Routes>
        </div>
      </div>
      <footer className='footer'>
        页脚
      </footer>

    </div>
  )
}
export default App

但是有些就喜欢使用类组件,那么我们还有第二种方式封装一个高阶组件在类组件中使用

下面继续改造App.jsx文件 将导航抽取走

新建HeaderNav.jsx导航文件

jsx 复制代码
import React, { PureComponent } from 'react'
import { useNavigate } from 'react-router-dom'

export class HeaderNav extends PureComponent {
  render() {
    const { navigate } = this.props.router
    return (
      <div>
        <button onClick={e => navigate("/home")}>首页</button>
        <button onClick={e => navigate("/about")}>关于</button>
        <button onClick={e => navigate("/user")}>我的</button>
      </div>
    )
  }
}
//高阶组件可以进行拆分
function HocWithRouter(WrapperComponent) {
  return function (props) {
    const navigate = useNavigate()
    const router = { navigate }
    return <WrapperComponent {...props} router={router} />
  }
}

export default HocWithRouter(HeaderNav)

这样就实现了功能。

四、Router的参数传递

4.1 动态路由传参使用Hooks-useParams

动态路由的概念指的是路由中的路径并不固定:

  • /search的path对应一个组件Search;
  • 如果我们将path在Route匹配时写成/search/:id,那么 /search/abc/search/123都可以匹配到该Route,并且进行显示;
  • 这个匹配规则,我们就称之为动态路由; 通常情况下,使用动态路由可以为路由传递参数。 App.jsx
jsx 复制代码
import { Navigate, Route, Routes,NavLink } from 'react-router-dom'
import Search from './pages/Search'
import "./style.css"
export function App() {

  return (
    <div className='app'>
      <h3>
        <NavLink to="/search/ceshi" > 动态路由</NavLink>------
      </h3>
      展示路由
      <div style={{ border: "1px solid red", height: "200px" }}>
        <Routes>
          <Route path='/' element={<Navigate to="/search" />}></Route>
          <Route path='/search/:id' element={<Search/>}></Route>
          
        </Routes>
      </div>

    </div>
  )
}

export default App

Search.jsx

jsx 复制代码
import React, { PureComponent } from 'react'
import { useNavigate, useParams } from 'react-router-dom';
//高阶组件可以进行拆分
function hocWithRouter(WrapperComponent) {
  return function (props) {
    const navigate = useNavigate()
    const params = useParams()
    const router = { navigate,params }
    return <WrapperComponent {...props} router={router} />
  }
}

export class Search extends PureComponent {
  render() {
    const { params }=this.props.router
    return (
      <div>
        我是搜索界面
        参数传递:{params.id}
      </div>
    )
  }
}

export default hocWithRouter(Search) 

4.2 search传递参数 Hooks-useLocation

App.jsx

jsx 复制代码
import {  Route, Routes, NavLink } from 'react-router-dom'

import Detail from './pages/Detail'
export function App() {

  return (
    <div className='app'>
      <h3>
        <NavLink to="/detail?name=林夕&age=18">search传参数</NavLink>
      </h3>
      展示路由
      <div style={{ border: "1px solid red", height: "200px" }}>
        <Routes>
          <Route path='/detail' element={<Detail />}></Route>
        </Routes>
      </div>

    </div>
  )
}

export default App

Detail.jsx

jsx 复制代码
import React, { PureComponent } from 'react'
import { useNavigate, useParams, useLocation, useSearchParams } from 'react-router-dom';

export class Detail extends PureComponent {
  render() {
    const { location,query } = this.props.router
    return (
      <div>
        {query.name}-{query.age}
      </div>
    )
  }
}
//高阶组件使用Hooks
function hocWithRouter(WrapperComponent) {
  return function (props) {
    const navigate = useNavigate()
    // 动态路由
    const params = useParams()
    // 查询字符串参数
    const location = useLocation()
    const [searchParams]=useSearchParams()
    const query=Object.fromEntries(searchParams)
    console.log(query);
    const router = { navigate, params, location,query }
    return <WrapperComponent {...props} router={router} />
  }
}
export default hocWithRouter(Detail) 

五、Router的配置方式- useRoutes

将上文的Routes和Route转化成单独的js文件去,同时使用Hooks

jsx 复制代码
<Routes> 
    <Route path='/' element={<Navigate to="/home"/>}></Route> 
    <Route path='/home' element={<Home />}></Route> 
    <Route path='/about' element={<About />}></Route> 
    <Route path='/user' element={<User />}></Route>
    <Route path='*' element={<div>页面丢失了</div>}></Route> 
</Routes>

抽取成单独的js文件 新建router.js

js 复制代码
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'
import HomeSon1 from './pages/HomeSon1'
import HomeSon2 from './pages/HomeSon2'
import { Navigate } from 'react-router-dom'

const routes=[
  {
    path:"/",
    element:<Navigate to="/home" />
  },
  {
    path:"/home",
    element:<Home />,
    children:[
      {
        path:"/home",
        element:<Navigate to="/home/HomeSon1" />
      },
      {
        path:"/home/HomeSon1",
        element:<HomeSon1 />
      },
      {
        path:"/home/HomeSon2",
        element:<HomeSon2 />
      },
    ]
  },
  {
    path:"/about",
    element:<About/>
  },
  {
    path:"/user",
    element:<User />
  },
  {
    path:"*",
    element:<div>页面丢失了</div>
  },
]

export default routes

App.jsx

jsx 复制代码
import routes from './router'
//...other code
展示路由
<div style={{ border: "1px solid red", height: "200px" }}>
    {useRoutes(routes)}
</div>
相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排6 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60617 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角8 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!8 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen10 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马11 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒11 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端