• 4. react路由


    1. 相关概念

    1.1 SPA

    整个应用只有一个完整的页面,称为单页Web应用。

    点击页面链接时不会刷新页面,只会做页面的局部刷新。

    数据通过ajax请求获取,在前端异步展现。

    1.2 路由

    一个路由就是一个映射关系(key: value),key为路径,value为function或component。

    路由分类

    1. 后端路由

      • value 是 function,用来处理客户端提交的请求
      • 注册路由:router.get(path, function(req, res)
      • 工作过程:当node接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求。返回响应数据
    2. 前端路由

      • 浏览器端路由,value 是 component,用于展示页面内容
      • 注册路由:
      • 工作过程:当浏览器的 path 为 /test 时,当前的路由组件就会变为 Test 组件

    1.3 react-router-dom

    1. react的一个插件库
    2. 专门用来实现一个 SPA 应用

    2. react-router-dom 相关API

    前端路由的原理主要是靠BOM的history。

    https://react-router.docschina.org/web/guides/philosophy

    2.1 基本使用

    安装库:npm i react-router-dom

    1. 引入标签
    import { Link, BrowserRouter, Route } from 'react-router-dom'
    
    • 1
    1. 使用路由链接切换组件,需要将Link标签放在BrowserRouter或HashRouter路由器中
    <div>
    	{/* 使用路由链接切换组件 */}
    	<BrowserRouter>
    		<Link className="list-group-item" to="/about">AboutLink>
    		<Link className="list-group-item" to="/home">HomeLink>
    	BrowserRouter>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 注册路由,实现根据路由链接展示不同组件
    <div>
    	{/* 注册路由--编写路由链接 */}
    	<BrowserRouter>
    		<Route path="/about" component={About} />
    		<Route path="/home" component={Home} />
    	BrowserRouter>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    问题:此时点击切换组件时,只有链接变化,组件不会变化。
    原因:两者分别用BrowserRouter包裹,意味着它们位于两个路由器当中,是相互独立的。
    解决:使用一个路由器包裹全部内容

    1. 在React中,所有的组件都是的组件,所以可以直接在index.js文件中用路由器将包裹起来。
    // 引入路由器
    import { BrowserRouter } from 'react-router-dom'
    
    // 渲染 App 到页面
    ReactDOM.render(
        <BrowserRouter>
            <App />
        </BrowserRouter>,
        document.getElementById('root')
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    HashRouter可以用于解决一些路径错误相关的问题。


    2.2 路由组件和一般组件

    一般组件:

    • 放在 components 文件夹中
    • 书写方式为
    • 写组件标签时传递了什么,就能接收到对应的props

    路由组件:

    • 放在 pages 文件夹中
    • 书写方式为
    • 路由组件会收到路由器传递的三个固定属性:history、location、match
      在这里插入图片描述
      在这里插入图片描述

    2.3 NavLink

    选中标签时为其添加高亮效果。

    设计原理:动态添加样式类名active。

    1. 使用NavLink

    使用Link:

    在这里插入图片描述

    使用NavLink:

    在这里插入图片描述

    可以在NavLink中传递属性activeClassName来实现点击时加哪一个样式的类名。

    {/* 相当于不写activeClassName,因为原本加的类名就是active */}
    <NavLink activeClassName='active' className="list-group-item" to="/about">AboutNavLink>
    
    • 1
    • 2
    <NavLink activeClassName='addactive' className="list-group-item" to="/about">AboutNavLink>
    <NavLink activeClassName='addactive' className="list-group-item" to="/home">HomeNavLink>
    
    • 1
    • 2
    .addactive {
        background-color: bisque !important;
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    2. 封装NavLink

    二次封装后可以减少代码内的重复内容。

    1. 新建一个一般组件MyNavLink
    2. 实现MyNavLink
    import React, { Component } from 'react';
    import { NavLink } from 'react-router-dom'
    
    export default class MyNavLink extends Component {
        render() {
            // 获取传递过来的props值
            const { to, title } = this.props
            return (
                <NavLink activeClassName='addactive' className="list-group-item" to={to}>{title}</NavLink>
            );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 使用MyNavLink
    <div className="list-group">
    	{/* 使用路由链接切换组件 */}
    	{/* <NavLink activeClassName='addactive' className="list-group-item" to="/about">AboutNavLink>
    	<NavLink activeClassName='addactive' className="list-group-item" to="/home">HomeNavLink> */}
    	<MyNavLink to="/about" title="About" />
    	<MyNavLink to="/home" title="Home" />
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 如果想在代码中传递多个标签属性,并且将标题做为标签体内容,可以优化代码如下:

    标签体内容其实也包含在props中,是标签的一个特殊属性:
    在这里插入图片描述

    <MyNavLink to="/about">AboutMyNavLink>
    <MyNavLink to="/home">HomeMyNavLink>
    
    • 1
    • 2
    import React, { Component } from 'react';
    import { NavLink } from 'react-router-dom'
    
    export default class MyNavLink extends Component {
        render() {
            // 获取传递过来的props值
            return (
                <NavLink activeClassName='addactive' className="list-group-item" {...this.props} />
            );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.4 Switch

    当页面中注册的路由非常多时,如果已经匹配到了目标路径,就应该停止往下查找,避免产生过多的消耗性能。

    <Route path="/about" component={About} />
    <Route path="/home" component={Home} />
    <Route path="/home" component={Test} />
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    此时可以使用Switch标签将所有的路由包裹起来,实现单一匹配,从而提高效率。

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

    在这里插入图片描述


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

    1. 模糊匹配:在V5版本中,如果最开始能匹配上,那就能得到结果;而V6版本的模糊匹配需要写成/home/*

    在这里插入图片描述
    在这里插入图片描述
    2. 严格匹配:使用exact属性可以开启精准匹配

    在这里插入图片描述
    在这里插入图片描述

    注意:
    严格匹配不要随意开启,否则有时会导致无法继续匹配二级路由


    2.6 Redirect

    重定向放在路由注册最下方,如果前面的路由都没有匹配上,则由重定向决定路由指向。

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

    2.7 嵌套路由

    在注册子路由时要写上父路由的path值,路由的匹配是按照注册路由的顺序进行的。

    在Home组件中添加子组件:

    <div>
        <h2>我是Home的内容h2>
        <div>
            <ul className="nav nav-tabs">
                <li>
                    <MyNavLink to="/home/news">NewsMyNavLink>
                li>
                <li>
                    <MyNavLink to="/home/message">MessageMyNavLink>
                li>
            ul>
            {/* 注册路由 */}
            <Switch>
                <Route path="/home/news" component={News} />
                <Route path="/home/message" component={Message} />
            Switch>
        div>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    {/* 注册路由--编写路由链接 */}
    <Switch>
    	<Route path="/about" component={About} />
    	<Route path="/home" component={Home} />
    	<Redirect to="/home/news" />
    Switch>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    2.8 路由组件传参

    1. 向路由组件传递params参数

    父组件向路由组件传递params参数:

    在这里插入图片描述

    新建Detail子组件,并在组件中接收params参数:

    import React, { Component } from 'react';
    
    const detailData = [
        { id: '01', content: 'aaa' },
        { id: '02', content: 'bbb' },
        { id: '03', content: 'ccc' }
    ]
    export default class Detail extends Component {
        render() {
            const { id, title } = this.props.match.params
            const findResult = detailData.find((detailObj) => {
                return detailObj.id == id
            })
            return (
                <ul>
                    <li>ID:{id}</li>
                    <li>TITLE:{title}</li>
                    <li>CONTENT:{findResult.content}</li>
                </ul>
            );
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    向路由组件传递params参数后,子组件中能通过this.props获取到传递的值

    在这里插入图片描述


    2. 向路由组件传递search参数

    向路由组件传递search参数

    在这里插入图片描述

    在子组件中获取到的search参数为以下效果,需要对其进行处理才能使用

    在这里插入图片描述
    引入qs库,能将urlencoded(即key=value&key=value格式)转为对象形式

    import React, { Component } from 'react';
    import qs from 'qs'
    
    const detailData = [
        { id: '01', content: 'aaa' },
        { id: '02', content: 'bbb' },
        { id: '03', content: 'ccc' }
    ]
    export default class Detail extends Component {
        render() {
            // 接收search参数
            const { search } = this.props.location
            const { id, title } = qs.parse(search.slice(1))
    
            const findResult = detailData.find((detailObj) => {
                return detailObj.id == id
            })
            return (
                <ul>
                    <li>ID:{id}</li>
                    <li>TITLE:{title}</li>
                    <li>CONTENT:{findResult.content}</li>
                </ul>
            );
        }
    }
    
    • 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

    3. 向路由组件传递state参数

    向路由组件传递state参数

    在这里插入图片描述

    子组件中接收值:

    import React, { Component } from 'react';
    
    const detailData = [
        { id: '01', content: 'aaa' },
        { id: '02', content: 'bbb' },
        { id: '03', content: 'ccc' }
    ]
    export default class Detail extends Component {
        render() {
            // 接收state参数
            const { id, title } = this.props.location.state || {} 
    
            const findResult = detailData.find((detailObj) => {
                return detailObj.id == id
            }) || {}
            return (
                <ul>
                    <li>ID:{id}</li>
                    <li>TITLE:{title}</li>
                    <li>CONTENT:{findResult.content}</li>
                </ul>
            );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    当页面刷新的时候也可以保留参数


    2.9 push和replace

    • push跳转:形成history,可返回上一级
    • replace跳转:不会形成history,不可返回到上一级。适用于登录后,不需要重新回到登录页面。

    用法:直接在路由标签中写replace属性


    2.10 编程式路由导航

    可以借助路由身上的history对象上的API来实现路由跳转:

    • this.props.history.push()
    • this.props.history.replace()
    • this.props.history.goBack()
    • this.props.history.goForward()
    • this.props.history.go()

    例子:

    export default class Message extends Component {
        state = {
            messageArr: [
                { id: '01', title: '消息1' },
                { id: '02', title: '消息2' },
                { id: '03', title: '消息3' },
            ]
        }
    
        replaceShow = (id, title) => {
            // replace跳转 + 携带params参数
            // this.props.history.replace(`/home/message/detail/${id}/${title}`)
    
            // replace跳转 + 携带search参数
            // this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
    
            // replace跳转 + 携带state参数
            this.props.history.replace(`/home/message/detail`, { id, title })
        }
    
        pushShow = (id, title) => {
            // push跳转 + 携带params参数
            // this.props.history.push(`/home/message/detail/${id}/${title}`)
    
            // push跳转 + 携带search参数
            // this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
    
            // push跳转 + 携带state参数
            this.props.history.push(`/home/message/detail`, { id, title })
        }
    
        render() {
            const { messageArr } = this.state;
            return (
                <div>
                    <ul>
                        {
                            messageArr.map((msgObj) => {
                                return (
                                    <li key={msgObj.id}>
                                        {/* 向路由组件传递params参数 */}
                                        {/* {msgObj.title} */}
    
                                        {/* 向路由组件传递search参数 */}
                                        {/* {msgObj.title} */}
    
                                        {/* 向路由组件传递search参数 */}
                                        <Link to={{ pathname: "/home/message/detail", state: { id: msgObj.id, title: msgObj.title } }}>{msgObj.title}</Link>
    
                                        &nbsp;<button onClick={() => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
                                        &nbsp;<button onClick={() => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button>
                                    </li>
                                )
                            })
                        }
                    </ul>
                    <hr />
                    {/* 接收params参数 */}
                    {/*  */}
    
                    {/* 接收search / state参数 */}
                    <Route path="/home/message/detail" component={Detail}></Route>
                </div>
            );
        }
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    const detailData = [
        { id: '01', content: 'aaa' },
        { id: '02', content: 'bbb' },
        { id: '03', content: 'ccc' }
    ]
    export default class Detail extends Component {
        render() {
            // 接收params参数
            // const {id, title} = this.props.match.params
    
            // 接收search参数
            // const { search } = this.props.location
            // const { id, title } = qs.parse(search.slice(1))
    
            // 接收state参数
            const { id, title } = this.props.location.state || {}
    
            const findResult = detailData.find((detailObj) => {
                return detailObj.id == id
            }) || {}
            return (
                <ul>
                    <li>ID:{id}</li>
                    <li>TITLE:{title}</li>
                    <li>CONTENT:{findResult.content}</li>
                </ul>
            );
        }
    }
    
    • 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

    2.11 withRouter的使用

    问题:一般组件当中直接调用this.props.history结果为空,无法使用路由组件中的API

    解决:使用withRouter加工一般组件,让一般组件具备路由组件所特有的API,其返回值是一个新组件

    import React, { Component } from 'react';
    // 引入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>
            <button onClick={this.forward}>前进</button>
            <button onClick={this.go}>go</button>
          </div>
        );
      }
    }
    
    // withRouter能接收一般组件,并在一般组件身上加上路由组件特有的属性
    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
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 相关阅读:
    手把手带你学python—牛客网python 机器学习 使用梯度下降对逻辑回归进行训练
    Java面试之JDK、JRE、JVM区别
    计算机毕业设计之java+javaweb的理发店管理系统
    ESP8266与STC8H8K单片机联动——天气时钟
    22-06-28 西安 redis(02) 持久化机制、入门使用、事务控制、主从复制机制
    C语言中,基本数据类型介绍
    年产3000吨冲压型果味硬糖生产车间工艺设计
    Selenium实现自动登录163邮箱和Locating Elements介绍
    集合体系图
    在线相册的前后端交互
  • 原文地址:https://blog.csdn.net/qq_46025031/article/details/125818957