Haoyuan's blog Haoyuan's blog
首页
导航站
  • Java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • Spring Cloud
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
留言区
GitHub (opens new window)

Somnus Haoyuan

Word is cheap, show me the code.
首页
导航站
  • Java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • Spring Cloud
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
留言区
GitHub (opens new window)
  • 超文本标记语言 - Html

  • 解释编程语言 - JavaScript

  • JS 超集语言 - TypeScript

  • 界面构建框架 - React

    • React - 基础与核心
    • React - 脚手架及使用
    • React - 路由
      • 路由的概念
        • 什么是路由?
        • 路由分类
        • 路由基石
        • 路由原理
      • 路由种类
      • 路由的基本使用
      • 路由组件与一般组件区别
      • Link与NavLink
      • 自定义MyNavLink
      • Switch的使用
      • 解决多级路径刷新页面样式丢失的问题
      • 路由的严格匹配与模糊匹配
      • Redirect的使用(指定初始路由)
      • 嵌套路由(多级路由)
      • 向路由组件传递参数
        • params参数
        • search参数(可以认为是伪query参数)
        • state参数
        • 对比
      • 编程式路由导航
      • withRouter的使用
      • BrowserRouter与HashRouter的区别
      • antd的按需引入+自定主题
    • React - Redux
    • React - 函数类型
    • React - 进阶知识
    • React - 父子传值与循环key
  • 渐进式框架 - Vue2

  • 渐进式框架 - Vue3

  • 前端
  • 界面构建框架 - React
Haoyuan
2022-02-21
目录

React - 路由

  • 路由的概念
    • 什么是路由?
    • 路由分类
    • 路由基石
    • 路由原理
  • 路由种类
  • 路由的基本使用
  • 路由组件与一般组件区别
  • Link与NavLink
  • 自定义MyNavLink
  • Switch的使用
  • 解决多级路径刷新页面样式丢失的问题
  • 路由的严格匹配与模糊匹配
  • Redirect的使用(指定初始路由)
  • 嵌套路由(多级路由)
  • 向路由组件传递参数
    • params参数
    • search参数(可以认为是伪query参数)
    • state参数
    • 对比
  • 编程式路由导航
  • withRouter的使用
  • BrowserRouter与HashRouter的区别
  • antd的按需引入+自定主题

# 路由的概念

# 什么是路由?

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

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

# 路由分类

  1. 后端路由:

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

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

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

  2. 前端路由:

    • 浏览器端路由,value 是 component,用于展示页面内容。

    • 注册路由: <Route path="/test" component={Test}>

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

# 路由基石

  • history 模式(操作 H5 的 BOM 的 history 对象)

  • hash 模式(原有的 url 后面加入 /#/)

# 路由原理

  • push:在原来的 uri 后面追加新的地址,存入历史记录,可以返回上一个页面

  • replace:替换原来的 uri,不存入历史记录,无法返回上一个页面

  • goBack:返回上一个历史记录页面

  • forward:前往下一个历史记录页面

# 路由种类

  • react-router-dom:前端开发路由(我们用)

  • react-router-native:基于 React 原生开发路由

  • react-router-any:任何环境开发路由

# 路由的基本使用

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

  2. 导航区的 a 标签改为 Link 标签

    <Link to="/xxxxx">Demo</Link>
    
    1
  3. 展示区写 Route 标签进行路径的匹配

    <Route path='/xxxx' component={Demo}/>
    
    1
  4. App.jsx 文件的 <App> 的最外侧包裹了一个 <BrowserRouter> 或 <HashRouter>

    import About from './components/About'
    render(){
        return (
            <div>
                <div>
                    <Link className="list-group-item" to="/about">About</Link>    
                </div>
                <div>
                    <Route path="/about" component={About}/>
                </div>
            </div>
        )
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 路由组件与一般组件区别

  • 写法不同:

    • 一般组件:<Demo/>

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

  • 存放文件夹位置不同:

    • 一般组件:components

    • 路由组件:pages

  • 接收到的 props 不同:

    • 一般组件:写组件标签时传递了什么,就能收到什么

    • 路由组件:接收到三个固定的属性(history,location,match),如下:

// 建议往下面看到编程式路由导航,再回来看更清楚和理解
history:
    go: ƒ go(n)   // 跳转 n 个页面,n 为正负数值
    goBack: ƒ goBack()  // 后退一个页面
    goForward: ƒ goForward()  // 前进一个页面
    push: ƒ push(path, state)  // push 模式,并且可以携带 state 参数
    replace: ƒ replace(path, state)  // replace 模式,并且可以携带 state 参数
location:
    pathname: "/about"
    search: ""     // 存有 search 参数,是一个字符串形式
    state: undefined   // 存有 state 参数
match:    // 存有params参数
    params: {}
    path: "/about"
    url: "/about"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Link与NavLink

  • 如果给 Link 加入点击高亮,需要换更高级的标签

  • NavLink 可以实现路由链接的高亮,通过 activeClassName 指定样式名,不加默认点击时自动追加 active

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

# 自定义MyNavLink

原因:NavLink 出现多个时,重新写属性不优雅,自己封装一个 MyNavLink,把重复的样式如 className 放入。

  1. 组件可以传入属性值,如 <MyNavLink to="/about" a={1} b={2}></MyNavLink>

  2. 组件也可以传入标签体内容,如 <MyNavLink>标签体内容</MyNavLink>

    • 标签体内容是特殊的属性值,所以标签属性值 也可以写标签体内容,如 <MyNavLink children="标签体内容"></MyNavLink>
  3. 传入的属性值存放在 props 里,传入的标签体存放在 props 的 children 里

    • 自定义 MyNavLink:( {...this.props} ),会把 props 里的属性解析出来展示

      render() {
          // console.log(this.props);  //有 to 属性,也有 children 对象的 About、Home 属性
          return (
              // <NavLink activeClassName="kele" className="list-group-item" {...this.props}>{this.props.children}</NavLink>
              //...this.props 有 to 属性,也有 children 对象的 About、Home 属性
              <NavLink activeClassName="kele" className="list-group-item" {...this.props}/>
          )
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
    • 引入:

      <MyNavLink to="/about">About</MyNavLink>
      <MyNavLink to="/home">Home</MyNavLink>
      
      1
      2

# Switch的使用

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

  2. 如果 path 出现多个一样的路径,则都会匹配上,但是中间出现多个会影响效率

    <Switch>
        <Route path="/about" component={About}/>
        <Route path="/demo" component={Demoe}/>
        <Route path="/kele" component={Kele}/>
        <Route path="/About" component={Test}/>
    </Switch>
    
    1
    2
    3
    4
    5
    6
  3. Switch 可以提高路由匹配效率(单一匹配),只要找到第一个匹配的 path,则不会往下找。

    <Switch>
        <Route path="/about" component={About}/>
        <Route path="/demo" component={Demoe}/>
        <Route path="/kele" component={Kele}/>
        <Route path="/About" component={Test}/>
    </Switch>
    
    1
    2
    3
    4
    5
    6

# 解决多级路径刷新页面样式丢失的问题

原因:当引入的 Css 或者其他文件有 ./ 开头,则默认去往前路由路径下找文件,其实文件都是存在于根目录下的 public 文件

解决:

  • public/index.html 中引入样式时不写 ./ ,写 / (常用),因为 / 代表当前根目录

  • public/index.html 中引入样式时不写./ ,写 %PUBLIC_URL%(常用)

  • 使用 HashRouter

# 路由的严格匹配与模糊匹配

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

    {/* 可以匹配 */}
    <BrowserRouter>
        <MyNavLink to="/home/a/b">Home</MyNavLink>
        <Route path="/home" component={Home}/>
    </BrowserRouter>
    {/* 不可以匹配 */}
    <BrowserRouter>
        <MyNavLink to="/home">Home</MyNavLink>
        <Route path="/home/a/b" component={Home}/>
    </BrowserRouter>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. 使用 exact 属性开启严格匹配(exact 或者 exact={true}):

    <BrowserRouter>
        <MyNavLink to="/home/a/b">Home</MyNavLink>
        <Route exact path="/home" component={Home}/>
    </BrowserRouter>
    
    1
    2
    3
    4
  3. 严格匹配不要随便开启,默认不要开,只有需要再开,有些时候开启会导致无法继续匹配二级路由

# Redirect的使用(指定初始路由)

  1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Redirect 指定的路由,可用于初次进入首页,打开默认组件

  2. 具体编码(进入首页,显示 About 组件的内容):

    <Switch>
        <Route path="/about" component={About}/>
        <Route path="/home" component={Home}/>
        <Redirect to="/about"/>
    </Switch>
    
    1
    2
    3
    4
    5

# 嵌套路由(多级路由)

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

    原因:不写父路由,默认只找子路由的 path,但是第一批注册的路由没有该 path,只有父路由的 path

  2. 路由的匹配是按照注册路由的顺序进行的

    { /* 父路由组件:*/ }
    <BrowserRouter>
        <MyNavLink to="/home">Home</MyNavLink>
        <Route path="/home" component={Home}/>
    </BrowserRouter>
    { /* 子路由组件:*/ }
    <BrowserRouter>
    		<MyNavLink to="/home/message">Message</MyNavLink>
    		<Route path="/home/news" component={News}/>
    </BrowserRouter>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 向路由组件传递参数

# params参数

  1. 路由链接(携带参数):

    <Link to='/demo/test/tom/18'}>详情</Link>
    
    1
  2. 注册路由(声明接收):

    <Route path="/demo/test/:name/:age" component={Test}/>
    
    1
  3. 接收参数:

    const {name,age} = this.props.match.params
    
    1

# search参数(可以认为是伪query参数)

  1. 路由链接(携带参数):

    <Link to='/demo/test?name=tom&age=18'}>详情</Link>
    
    1
  2. 注册路由(无需声明,正常注册即可):

    <Route path="/demo/test" component={Test}/>
    
    1
  3. 接收参数(字符串形式:?key=value$......,没有解析为 {key:value,......}):

    import qs from 'querystring'
    ......
    const search = this.props.location.search  //?name=tom&age=18  字符串
    const {id,title} = qs.parse(search.slice(1));	//把?去掉
    
    1
    2
    3
    4

    备注:获取到的 search 是 urlencoded 编码字符串,需要借助第三方库 querystring 或者自己解析!

# state参数

  1. 路由链接(携带参数,参数必须为对象,对象包含 子路由路径和要传的参数):

    <Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
    
    1
  2. 注册路由(无需声明,正常注册即可):

    <Route path="/demo/test" component={Test}/>
    
    1
  3. 接收参数:

    const {name,age} = this.props.location.state || {}
    
    1

    备注:url 地址没有参数,类似于 post 提交后的 url 地址,而且刷新也可以保留住参数!

    原理:BrowserRouter 有 history 历史记录对象,该对象存有参数的记录,如果清除历史记录缓存,那么刷新会丢失参数!

    所以 HashRouter 会刷新丢失参数,具体看 BrowerRouter与 HashRouter 的区别

# 对比

params 参数用的最多,其次是 search 参数,最后是 state 参数,如果不想地址栏有参数显示,就使用 state 参数。

# 编程式路由导航

  1. 概念:不需要借助 Link 或者 NavLink 标签进行路由跳转、传参

  2. 方法:利用点击事件,借助 this.props.history 对象上的 API 对操作路由跳转、前进、后退,加入

    // 往后看'路由组件和一般组件区别',里面有更详细的介绍
    this.props.history.push(uri,state)  // uri 后追加 path,产生历史记录,可携带 state 参数
    this.props.history.replace(uri,state)  // 替换原来的 path,不产生历史记录,可携带 state 参数
    this.props.history.goBack()  // 后退
    this.props.history.goForward()  // 前进
    this.props.history.go(n)  // 跳转 n 个页面,n 为正负数值
    
    // 例子
    export default class Message extends Component{
        state = {
            messageArr:[
                {id:'01',title:'消息1'},
                {id:'02',title:'消息2'},
                {id:'03',title:'消息3'},
            ]
        }
        replaceShow = (id,title)=>{
            //replace 跳转 + 携带 state 参数
            this.props.history.replace(`/home/message/detail`,{id,title})
        }
        pushShow = (id,title)=>{
            //push 跳转 + 携带 state 参数
            this.props.history.push(`/home/message/detail`,{id,title})
        }
        render() {
            return (
                <button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button>  
                <button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
            )
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

# withRouter的使用

  1. withRouter 可以加工一般组件,让一般组件具备路由组件所特有的 API

  2. withRouter 的返回值是一个新组件

import {withRouter} from 'react-router-dom'
class Header extends Component {
	back = ()=>{
		this.props.history.goBack()
	}
	forward = ()=>{
		this.props.history.goForward()
	}
	go = ()=>{
		this.props.history.go(-2)
	}
	render() {
		return (
			<div className="page-header">
				<h2>React Router Demo</h2>
				<button onClick={this.back}>回退</button>&nbsp;
				<button onClick={this.forward}>前进</button>&nbsp;
				<button onClick={this.go}>go</button>
			</div>
		)
	}
}

export default withRouter(Header)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 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 参数的丢失

  4. 备注:HashRouter 可以用于解决一些路径错误相关的问题

# antd的按需引入+自定主题

  1. 安装依赖:

    yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
    
    1
  2. 修改package.json

    ....
    "scripts": {
        "start": "react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-scripts eject"
    },
    ....
    
    1
    2
    3
    4
    5
    6
    7
    8
  3. 根目录下创建 config-overrides.js

    //配置具体的修改规则
    const { override, fixBabelImports,addLessLoader} = require('customize-cra');
    module.exports = override(
        fixBabelImports('import', {
            libraryName: 'antd',
            libraryDirectory: 'es',
            style: true,
        }),
        addLessLoader({
            lessOptions:{
                javascriptEnabled: true,
                modifyVars: { '@primary-color': 'green' },
            }
        }),
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css' 应该删掉。

编辑此页 (opens new window)
#React
更新时间: 2025/04/06, 01:04:59
React - 脚手架及使用
React - Redux

← React - 脚手架及使用 React - Redux→

最近更新
01
技术随笔 - Element Plus 修改包名 原创
11-02
02
Reactor - 扩展性
11-02
03
Reactor - 最佳实践
11-02
更多文章>
Theme by Vdoing | Copyright © 2021-2025 Somnus Haoyuan | MIT License
京公网安备 11010802034042号 京ICP备2025120692号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式