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 可以用于解决一些路径错误相关的问题

相关推荐
一ge科研小菜鸡11 分钟前
React前端框架:现代网页开发的基石(附带构建简单任务管理应用案例代码)
前端框架
熊的猫40 分钟前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
瑶琴AI前端1 小时前
uniapp组件实现省市区三级联动选择
java·前端·uni-app
会发光的猪。1 小时前
如何在vscode中安装git详细新手教程
前端·ide·git·vscode
我要洋人死2 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人3 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人3 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR3 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香3 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596933 小时前
前端预览word、excel、ppt
前端·word·excel