07_React 路由

React 路由(5.x版本)

  • 一、相关理解
    • [1、SPA 的理解](#1、SPA 的理解)
    • 2、路由的理解
      • [2.1 什么是路由?](#2.1 什么是路由?)
      • [2.2 路由分类](#2.2 路由分类)
        • [2.2.1 后端路由](#2.2.1 后端路由)
        • [2.2.2 前端路由](#2.2.2 前端路由)
    • [3、react-router-dom(Web 开发使用) 的理解](#3、react-router-dom(Web 开发使用) 的理解)
  • [二、react-router-dom 相关 API](#二、react-router-dom 相关 API)
    • 1、内置组件
      • [1.1 BrowserRouter](#1.1 BrowserRouter)
      • [1.2 HashRouter](#1.2 HashRouter)
      • [1.3 Route](#1.3 Route)
      • [1.4 Redirect](#1.4 Redirect)
      • [1.5 Link](#1.5 Link)
      • [1.6 NavLink](#1.6 NavLink)
      • [1.7 Switch](#1.7 Switch)
    • 2、路由的基本使用
    • 3、路由组件和一般组件
      • [3.1 路由组件](#3.1 路由组件)
      • [3.2 一般组件](#3.2 一般组件)
    • 4、解决多级路由下页面刷新样式丢失问题
    • 5、路由的模糊匹配与精确匹配
      • [5.1 匹配的路径需要的和顺序都对上,默认就是模糊匹配](#5.1 匹配的路径需要的和顺序都对上,默认就是模糊匹配)
      • [5.2 精确匹配 使用 exact 属性](#5.2 精确匹配 使用 exact 属性)
      • [5.3 如果模糊匹配引发异常,才开启 精确匹配,一般不会全部开启精确(严格)匹配](#5.3 如果模糊匹配引发异常,才开启 精确匹配,一般不会全部开启精确(严格)匹配)
      • [5.4 总结](#5.4 总结)
    • [6、Redirect 的使用](#6、Redirect 的使用)
    • 7、二级路由(嵌套路由)
    • 8、向路由组件传递参数
      • [8.1 params 参数](#8.1 params 参数)
      • [8.2 search 参数](#8.2 search 参数)
      • [8.3 state 参数(路由组件独有的 state)](#8.3 state 参数(路由组件独有的 state))
    • [9、push 和 replace](#9、push 和 replace)
    • 10、编程式路由导航
    • [11、withRouter 的使用](#11、withRouter 的使用)
    • [12、BrowserRouter 与 HashRouter 的区别](#12、BrowserRouter 与 HashRouter 的区别)

一、相关理解

1、SPA 的理解

1、单页 Web 应用(single page web application, SPA)

2、整个应用只有一个完整的页面

3、点击页面中的链接不会刷新 页面,只会做页面的局部更新

4、数据都需要通过 ajax 请求获取,并在前端异步展现

单页面,多组件

2、路由的理解

2.1 什么是路由?

1、一个路由就是一个映射关系(key:value)

2、key 为路径,value 可能是 function 或 component

2.2 路由分类

2.2.1 后端路由

1、理解: value 是 function ,用来处理客户端提交的请求。

2、注册路由:router.get(path,function(req,res))

3、工作过程:当 node 接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据

2.2.2 前端路由

1、浏览器端路由:value 是 component, 用于展示页面内容

2、注册路由:

3、工作过程:当浏览器的 path 变成 /test 时,当前路由组件就会变成 Test 组件

4、工作原理:底层原理依靠的是浏览器的 history(BOM 对象上面),专门用来管理路由的,原本底层的操作 API 过于繁琐,所以一般都使用三方封装好的。

history 模式,直接使用 H5 推出的 API,个别旧版本的浏览器可能不支持

hash 值(锚点)跳转不会刷新页面,也会留下历史记录。兼容性好

3、react-router-dom(Web 开发使用) 的理解

1、React 的一个插件库

2、专门用来实现一个 SPA 应用

3、基于 React 的项目基本都会用到此库

4、react-router 有三种:Web(适用 web 开发)、native(适用 React Native 开发)、any(适用任何场景)

5、路由器(router)是用来管理 路由(route) 的

二、react-router-dom 相关 API

npm i react-router-dom@5

1、内置组件

1.1 BrowserRouter

下面代码中相当于有两个 路由器包裹,所以没法统一管理

jsx 复制代码
<div className="row">
  <div className="sidebar">
    <BrowserRouter>
      <Link className="list-item active" to="/about">
        About
      </Link>
      <Link className="list-item" to="/home">
        Home
      </Link>
    </BrowserRouter>
  </div>
  <div className="panel">
    <BrowserRouter>
      <Route path="/about" component={About} />
      <Route path="/home" component={Home} />
    </BrowserRouter>
  </div>
</div>

1.2 HashRouter

路径都会有一个 # 号,后面的东西都不会作为资源发送给服务器端

1.3 Route

注册路由

1.4 Redirect

放在路由注册的最下方,如果所有的路由都没有匹配上,就跟着 redirect 走

原生 html 中,使用 a 标签跳转不同的页面

在 React 中靠路由链接实现切换组件---编写路由链接

Link 的属性跟 a 标签一致

外层需要 包裹 或者

没法高亮菜单

点击谁就会默认给谁追加一个 类样式名 .active

有一个 属性名 activeClassName='activeClass'

总结:

1、NavLink 可以实现路由链接的高亮,通过 activeClassName 指定样式名

2、组件标签,标签体内容是一个特殊的标签属性

3、通过 this.props.children 可以获取标签体内容

基本使用:

jsx 复制代码
import React, { Component } from 'react'

import { NavLink, Route } from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'

import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div className="row">
          <div className="sidebar">
            <NavLink
              activeClassName="activeClass"
              className="list-item"
              to="/about"
            >
              About
            </NavLink>
            <NavLink
              activeClassName="activeClass"
              className="list-item"
              to="/home"
            >
              Home
            </NavLink>
          </div>
          <div className="panel">
            <Route path="/about" component={About} />
            <Route path="/home" component={Home} />
          </div>
        </div>
      </div>
    )
  }
}

二次封装使用:

MyNavLink 组件

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

export default class MyNavLink extends Component {
  render() {
    let { to, pathName } = this.props
    return (
      <NavLink activeClassName="activeClass" className="list-item" to={to}>
        {pathName}
      </NavLink>
    )
  }
}

App 组件

jsx 复制代码
import React, { Component } from 'react'

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

import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'

import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div className="row">
          <div className="sidebar">
            <MyNavLink to="/about" pathName="About"></MyNavLink>
            <MyNavLink to="/home" pathName="Home"></MyNavLink>
            {/* <NavLink activeClassName='activeClass'  className="list-item"  to='/about' >About</NavLink>
              <NavLink activeClassName='activeClass'  className="list-item"  to='/home' >Home</NavLink> */}
          </div>
          <div className="panel">
            <Route path="/about" component={About} />
            <Route path="/home" component={Home} />
          </div>
        </div>
      </div>
    )
  }
}

MyNavLink 组件的第二种封装(更加简洁好用)

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

export default class MyNavLink extends Component {
  render() {
    return (
      <NavLink
        activeClassName="activeClass"
        className="list-item"
        {...this.props}
      />
    )
  }
}

App 组件

jsx 复制代码
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div className="row">
          <div className="sidebar">
            <MyNavLink to="/about">About</MyNavLink>
            <MyNavLink to="/home">Home</MyNavLink>
            {/* <NavLink activeClassName='activeClass'  className="list-item"  to='/about' >About</NavLink>
              <NavLink activeClassName='activeClass'  className="list-item"  to='/home' >Home</NavLink> */}
          </div>
          <div className="panel">
            <Route path="/about" component={About} />
            <Route path="/home" component={Home} />
          </div>
        </div>
      </div>
    )
  }
}

1.7 Switch

注册的路由一个以上可以包裹起来,这时候匹配到第一个就直接停止了

总结:

1、通常情况下,path 和 component 是一一对应的关系

2、Switch 可以提高路由匹配效率(单一匹配)

jsx 复制代码
import React, { Component } from 'react'

import { Route, Switch } from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'

import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div className="row">
          <div className="sidebar">
            <MyNavLink to="/about">About</MyNavLink>
            <MyNavLink to="/home">Home</MyNavLink>
            <MyNavLink to="/test">Test</MyNavLink>
            {/* <NavLink activeClassName='activeClass'  className="list-item"  to='/about' >About</NavLink>
              <NavLink activeClassName='activeClass'  className="list-item"  to='/home' >Home</NavLink> */}
          </div>
          <div className="panel">
            <Switch>
              <Route path="/about" component={About} />
              <Route path="/home" component={Home} />
              <Route path="/home" component={Test} />
            </Switch>
          </div>
        </div>
      </div>
    )
  }
}

2、路由的基本使用

1、明确好界面中的 导航区、展示区

2、导航区的 a 标签改为 Link 标签
Demo 3、展示区写 Route 标签进行路径的匹配 4、的最外层包裹了一个 或者

3、路由组件和一般组件

总结区别:

1、写法不同:

一般组件:

路由组件: <Route path="/demo" component={Demo}

2、存放位置不同:

一般组件:components

路由组件:pages

3、接收到的 props 不同:

一般组件:与组件标签时传递了什么,就接收什么

路由组件:接收到三个固定的属性

javascript 复制代码
{
    "history": {
        "action": "POP",
        "location": {
            "pathname": "/about",
            "search": "",
            "hash": "",
            "key": "mlt4yu"
        }
    },
    "location": {
        "pathname": "/about",
        "search": "",
        "hash": "",
        "key": "mlt4yu"
    },
    "match": {
        "path": "/about",
        "url": "/about",
        "isExact": true,
        "params": {}
    }
}

3.1 路由组件

规范些的写法是 放到 pages 中

不用传参数也可以收到 props

jsx 复制代码
<Route path="/about" component={About} />

3.2 一般组件

规范些的写法是 放到 components 中

没有传参就不会收到 props

jsx 复制代码
<Home />

4、解决多级路由下页面刷新样式丢失问题

BrowserRouter 刷新页面样式丢失,是出现在二级(多级)路由下,引入样式时会将路由路径添加进去,这时候样式就会丢失,react 就会返回 index.html 的内容回来

./ 以当前文件出发,在当前文件夹下面去找
解决方法 1:(去掉 .)

表示直接去 localhost:3000/css/bootstrap.css 查找

解决方法 2:(%PUBLIC_URL%)

绝对路径解决

解决方法 3:将路由模式改为 HashRouter

注意:包管理 npm 和 yarn 不要混着用,否则容易造成包的丢失

5、路由的模糊匹配与精确匹配

5.1 匹配的路径需要的和顺序都对上,默认就是模糊匹配

/home/a/b 和/home 就是可以匹配的

jsx 复制代码
import React, { Component } from 'react'

import { Route, Switch } from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'

import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div className="row">
          <div className="sidebar">
            <MyNavLink to="/about">About</MyNavLink>
            <MyNavLink to="/home/a/b">Home</MyNavLink>
            <MyNavLink to="/test">Test</MyNavLink>
            {/* <NavLink activeClassName='activeClass'  className="list-item"  to='/about' >About</NavLink>
              <NavLink activeClassName='activeClass'  className="list-item"  to='/home' >Home</NavLink> */}
          </div>
          <div className="panel">
            <Switch>
              <Route path="/about" component={About} />
              <Route path="/home" component={Home} />
              <Route path="/home" component={Test} />
            </Switch>
          </div>
        </div>
      </div>
    )
  }
}

5.2 精确匹配 使用 exact 属性

jsx 复制代码
import React, { Component } from 'react'

import { Route, Switch } from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'

import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div className="row">
          <div className="sidebar">
            <MyNavLink to="/about">About</MyNavLink>
            <MyNavLink to="/home/a/b">Home</MyNavLink>
            <MyNavLink to="/test">Test</MyNavLink>
            {/* <NavLink activeClassName='activeClass'  className="list-item"  to='/about' >About</NavLink>
              <NavLink activeClassName='activeClass'  className="list-item"  to='/home' >Home</NavLink> */}
          </div>
          <div className="panel">
            <Switch>
              <Route path="/about" exact component={About} />
              <Route path="/home" exact component={Home} />
              <Route path="/home" exact component={Test} />
            </Switch>
          </div>
        </div>
      </div>
    )
  }
}

5.3 如果模糊匹配引发异常,才开启 精确匹配,一般不会全部开启精确(严格)匹配

5.4 总结

1、默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序一致)

2、开启严格匹配

3、严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

6、Redirect 的使用

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Redirect 指定的路由

jsx 复制代码
import React, { Component } from 'react'

import { Route, Switch, Redirect } from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'

import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <div className="row">
          <div className="sidebar">
            <MyNavLink to="/about">About</MyNavLink>
            <MyNavLink to="/home/a/b">Home</MyNavLink>
            <MyNavLink to="/test">Test</MyNavLink>
            {/* <NavLink activeClassName='activeClass'  className="list-item"  to='/about' >About</NavLink>
              <NavLink activeClassName='activeClass'  className="list-item"  to='/home' >Home</NavLink> */}
          </div>
          <div className="panel">
            <Switch>
              <Route path="/about" component={About} />
              <Route path="/home" component={Home} />
              <Route path="/home" component={Test} />
              <Redirect to="/about" />
            </Switch>
          </div>
        </div>
      </div>
    )
  }
}

7、二级路由(嵌套路由)

React 中路由注册从 App 里面开始的,路由的匹配都是优先注册的先匹配

总结:

1、注册子路由时要写上父路由的 path 值

2、路由的匹配时按照注册路由顺序进行匹配

jsx 复制代码
import React, { Component } from 'react'

import { Route, Switch, Redirect } from 'react-router-dom'

import MyNavLink from '../../components/MyNavLink'
import News from './News'
import Message from './Message'

export default class Home extends Component {
  render() {
    return (
      <div>
        <h2>我是 Home 的页面内容</h2>
        <div className="sidebar2">
          <MyNavLink to="/home/news">news</MyNavLink>
          <MyNavLink to="/home/message">Message</MyNavLink>
        </div>

        <Switch>
          <Route path="/home/news" component={News} />
          <Route path="/home/message" component={Message} />
          <Redirect to="/home/news" />
        </Switch>
      </div>
    )
  }
}

8、向路由组件传递参数

8.1 params 参数

路由链接(携带参数):

jsx 复制代码
<Link to={`/home/message/detail/${el.id}`}>{el.title}</Link>

注册路由(声明接收):

jsx 复制代码
<Route path="/home/message/detail/:id" component={Detail} />

接收参数

jsx 复制代码
let { id } = this.props.match.params

不需要声明接收, 正常注册路由即可

路由链接(携带参数):

jsx 复制代码
<Link to={`/home/message/detail?id=${el.id}`}>{el.title}</Link>

注册路由(无需声明,正常注册即可):

jsx 复制代码
<Route path="/home/message/detail" component={Detail} />

接收参数

jsx 复制代码
this.props.location.search

备注:获取到的 search 时 urlencoded 编码字符串,需要借助 querystring 解析

8.3 state 参数(路由组件独有的 state)

BrowserRouter 一直维护 history,所以刷新页面 state 中的数据不会丢失。如果清空浏览器缓存和历史记录等才会没有 state。

路由链接(携带参数):

jsx 复制代码
<Link
  to={{
    pathname: '/home/message/detail',
    state: {
      id: el.id,
    },
  }}
>
  {el.title}
</Link>

注册路由(无需声明,正常注册即可):

jsx 复制代码
<Route path="/home/message/detail" component={Detail} />

接收参数

jsx 复制代码
this.props.location.state

备注:参数不体现在地址栏上面,但刷新也可以保留住参数

9、push 和 replace

默认时 push 模式,不做替换,能够留下所有的痕迹

开启 replace 模式:不留下痕迹

10、编程式路由导航

两种模式(push 和 replace)可以携带三种形式的参数,携带参数和路由注册以及接收参数都需要保持一致

借助 this.props.history 对象上的 API 进行前进、后退

jsx 复制代码
this.props.history.replace
this.props.history.push

this.props.history.push(path, state) // state 参数传递

this.props.history.goForward() //前进
this.props.history.goBack() // 后退
this.props.history.go() // 整数为前进 n 步,负数为后退 n 步,0为当前页刷新

11、withRouter 的使用

一般组件没有 history,也就是一般组件中不能用路由导航的 API

withRouter 能够接收一个一般组件,将一般组件加工后,能够拥有路由组件的 API

12、BrowserRouter 与 HashRouter 的区别

1、底层原理不一样

BrowserRouter 使用的是 H5 的 history API,不兼容 IE9 及以下版本

HashRouter 使用的 URL 的哈希值
2、path 表现形式不一样

BrowserRouter 的路径中没有 # ,例如: localhost:3000/demo/test

HashRouter 的路径中有 # ,例如: localhost:3000/#/demo/test
3、刷新后对路由 state 参数的影响

BrowserRouter 没有任何影响,因为 state 保存在 history 对象中

HashRouter 刷新后会导致路由 state 参数的丢失,因为它没有 history 对象
4、备注:HashRouter 可以用于解决一些路径错误相关的问题

相关推荐
下雪天的夏风6 分钟前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom17 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
volodyan21 分钟前
electron react离线使用monaco-editor
javascript·react.js·electron
Hello-Mr.Wang34 分钟前
vue3中开发引导页的方法
开发语言·前端·javascript
程序员凡尘1 小时前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
(⊙o⊙)~哦6 小时前
JavaScript substring() 方法
前端
无心使然云中漫步7 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者7 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_7 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js