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,用于展示页面内容
    • 注册路由:<Route path="/test" component={Test}>
    • 工作过程:当浏览器的 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. 使用路由链接切换组件,需要将Link标签放在BrowserRouter或HashRouter路由器中
<div>
	{/* 使用路由链接切换组件 */}
	<BrowserRouter>
		<Link className="list-group-item" to="/about">About</Link>
		<Link className="list-group-item" to="/home">Home</Link>
	</BrowserRouter>
</div>
  1. 注册路由,实现根据路由链接展示不同组件
<div>
	{/* 注册路由--编写路由链接 */}
	<BrowserRouter>
		<Route path="/about" component={About} />
		<Route path="/home" component={Home} />
	</BrowserRouter>
</div>

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

  1. 在React中,所有的组件都是<App />的组件,所以可以直接在index.js文件中用路由器将<App />包裹起来。
// 引入路由器
import { BrowserRouter } from 'react-router-dom'

// 渲染 App 到页面
ReactDOM.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>,
    document.getElementById('root')
);

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 文件夹中
  • 书写方式为 <Home />
  • 写组件标签时传递了什么,就能接收到对应的props

路由组件:

  • 放在 pages 文件夹中
  • 书写方式为 <Route path="/home" component={Home} />
  • 路由组件会收到路由器传递的三个固定属性:history、location、match
    在这里插入图片描述
    在这里插入图片描述

2.3 NavLink

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

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

1. 使用NavLink

使用Link:

在这里插入图片描述

使用NavLink:

在这里插入图片描述

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

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

在这里插入图片描述

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. 使用MyNavLink
<div className="list-group">
	{/* 使用路由链接切换组件 */}
	{/* <NavLink activeClassName='addactive' className="list-group-item" to="/about">About</NavLink>
	<NavLink activeClassName='addactive' className="list-group-item" to="/home">Home</NavLink> */}
	<MyNavLink to="/about" title="About" />
	<MyNavLink to="/home" title="Home" />
</div>
  1. 如果想在代码中传递多个标签属性,并且将标题做为标签体内容,可以优化代码如下:

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

<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
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} />
        );
    }
}

2.4 Switch

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

<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/home" component={Test} />

在这里插入图片描述

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

<Switch>
	<Route path="/about" component={About} />
	<Route path="/home" component={Home} />
	<Route path="/home" component={Test} />
</Switch>

在这里插入图片描述


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>

2.7 嵌套路由

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

在Home组件中添加子组件:

<div>
    <h2>我是Home的内容</h2>
    <div>
        <ul className="nav nav-tabs">
            <li>
                <MyNavLink to="/home/news">News</MyNavLink>
            </li>
            <li>
                <MyNavLink to="/home/message">Message</MyNavLink>
            </li>
        </ul>
        {/* 注册路由 */}
        <Switch>
            <Route path="/home/news" component={News} />
            <Route path="/home/message" component={Message} />
        </Switch>
    </div>
</div>
{/* 注册路由--编写路由链接 */}
<Switch>
	<Route path="/about" component={About} />
	<Route path="/home" component={Home} />
	<Redirect to="/home/news" />
</Switch>

在这里插入图片描述

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>
        );
    }
}

向路由组件传递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>
        );
    }
}

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>
        );
    }
}

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


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参数 */}
                                    {/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}

                                    {/* 向路由组件传递search参数 */}
                                    {/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}

                                    {/* 向路由组件传递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参数 */}
                {/* <Route path="/home/message/detail/:id/:detail" component={Detail} /> */}

                {/* 接收search / state参数 */}
                <Route path="/home/message/detail" component={Detail}></Route>
            </div>
        );
    }
}
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>
        );
    }
}

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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值