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>
相关推荐
傻虎贼头贼脑4 分钟前
day21JS-npm中的部分插件使用方法
前端·npm·node.js
low神15 分钟前
前端在网络安全攻击问题上能做什么?
前端·安全·web安全
因为奋斗超太帅啦25 分钟前
React学习笔记(三)——React 组件通讯
笔记·学习·react.js
qbbmnnnnnn1 小时前
【CSS Tricks】如何做一个粒子效果的logo
前端·css
唐家小妹1 小时前
【flex-grow】计算 flex弹性盒子的子元素的宽度大小
前端·javascript·css·html
涔溪1 小时前
uni-app环境搭建
前端·uni-app
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_032】5.4 Grid 网格布局的显示网格与隐式网格(上)
前端·css·css3·html5·网格布局·grid布局·css网格布局
洛千陨1 小时前
element-plus弹窗内分页表格保留勾选项
前端·javascript·vue.js
小小19921 小时前
elementui 单元格添加样式的两种方法
前端·javascript·elementui
前端没钱2 小时前
若依Nodejs后台、实现90%以上接口,附体验地址、源码、拓展特色功能
前端·javascript·vue.js·node.js