(五)React-router路由

🔥React全家桶🔥

React全家桶 地址
React全家桶(一)之React入门🔥 https://blog.csdn.net/article/124120080
React全家桶(二)之组件化编程🔥 https://blog.csdn.net/article/124194107
React全家桶(三)之React脚手架🔥 https://blog.csdn.net/article/124281588
React全家桶(四)之axios请求数据🔥 https://blog.csdn.net/article/124281650
React全家桶(五)之React-router路由🔥 https://blog.csdn.net/article/124453733
React全家桶(六)之redux状态管理🔥 https://blog.csdn.net/article/124454379

React 路由

目录总览:

[图片上传失败...(image-8f5282-1651062725158)]

路由基本概念

现代的前端应用大多都是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。为了有效的使用单个页面来管理原来多个页面的功能,前端路由应运而生。

SPA的概念

1.单页Web应用(single page web application,SPA)。
2.整个应用只有一个完整的html页面
3.点击页面中的链接不会刷新页面,只会做页面的局部更新
4.数据都需要通过ajax请求获取, 并在前端异步展现。

单页面+多组件

路由的基本概念与原理

路由概念

  • 路由是一个比较广义和抽象的概念,路由的本质就是对应关系
  • 一个路由就是一个映射关系,一个key对应一个value
  • key为路径, value可能是function或component

路由的分类:

  • 后端路由:(根据路径向服务器端请求对应数据)
    1)理解: value是function, 用来处理客户端提交的请求。
    2)注册路由: router.get(path, function(req, res))
    3)工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
  • 前端路由:(根据路径展示对应的组件)
    1)浏览器端路由,value是component,用于展示页面内容。
    2)注册路由: <Route path="/test" component={Test}>
    3)工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

react-router-dom

  • react的一个插件库。
  • 专门用来实现一个SPA应用。
  • 基于react的项目基本都会用到此库。

下载方式:install i react-router-dom`

路由的基本使用

一、路由组件(Route,Link)

1. 使用步骤

  1. 安装: npm add react-router-dom
  2. 导入路由的三个核心组件:BrowserRouter/Route/Link
  3. 使用Router组件包裹整个应用
  4. 使用Link组件作为导航菜单(路由入口)
  5. 使用Route组件配置路由规则和要展示的组件(路由出口)

[图片上传失败...(image-2d7173-1651062725158)]

2. 路由组件说明

  1. Router组件:包裹整个应用,一个React应用只需要使用一次
    • 两种常见Router:HashRouter、BrowserRouter
    • HashRouter: 使用URL的哈希值实现(localhost:3000/#/first)
    • 推荐使用BrowserRouter:使用H5的history api实现(localhost:3000/first)
  2. Link组件:用于指定导航链接(a标签)
    • to属性:浏览器地址栏中的pathname(location.pathname)
  3. Route组件:指定路由展示组件相关信息
    • path属性:路由规则
    • component属性:展示的组件
    • Route组件写在哪,渲染出来的组件就展示在哪

3. 路由的执行过程

最后,我们再对React路由实现的原理做一个小结:

(1)使用Link等标签实现对浏览器path的操作(本质上是对BOM对象的history进行操作)

(2)当前端路由器检测到浏览器的path发生了变化

(3)前端路由内部遍历所有Route组件,使用路由规则(path)与pathname进行匹配

(4)当路由规则(path)能匹配地址栏中的pathname时,就展示该Route组件的内容

[图片上传失败...(image-5f0857-1651062725158)]

路由组件与一般组件的不同之处:

1.写法不同:
一般组件: <Demo/>
路由组件: <Route path="/demo" component={Demo}/>
2.存放位置不同:
一般组件: components
路由组件: pages
3.接收到的props不同:
一般组件: 写组件标签时传递了什么,props就能收到什么
路由组件: 接收带三个固定的属性
history:
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
push: ƒ push(path, state)
replace: ƒ replace(path, state)
location:
pathname: "/home"
search: ""
state: undefined
match:
params: {}
path: "/home"
url: "/home"

路由组件与一般组件props接收的数据对比图:

[图片上传失败...(image-541ad4-1651062725158)]

二、React-route库的内置组件

1. NavLink组件

  • NavLink组件是在Link组件的基础上做了高亮特效的增强,activeClassName="active"

[图片上传失败...(image-553928-1651062725158)]

拓展:封装MyNavLink

[图片上传失败...(image-c0b9a3-1651062725158)]

NavLink总结:

NavLink与封装MyNavLink

  1. NavLink可以实现路由链接的高亮,通过activeclassName指定样式名
  2. 标签体内容是一个特殊的标签属性
  3. 通过this.props.children可以获取标签体内容

2. Switch的使用(单一匹配

当路由中出现了2个或者2个以上的path同时匹配的情况,那么实际上对应的路由组件都会被渲染。如果我们想要说只渲染(挂载)第一个匹配上的组件的话,那么我们可以使用<Switch>组件来解决。

  • 未使用Switch组件的情况: /home对应着2个组件
    [图片上传失败...(image-1f43f7-1651062725158)]

[图片上传失败...(image-1b137-1651062725158)]

  • 引入Switch组件的情况

[图片上传失败...(image-8b6d27-1651062725158)]

[图片上传失败...(image-6b28e3-1651062725158)]

Switch总结:

Switch的使用

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

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

3. Redirect (路由重定向)

我们在进入网站的时候,网站的导航栏中往往会有一个默认选中的标签,对于这种场景,React路由其实也为我们提供了对应的组件来帮助我们简化开发,那就是Redirect组件。我们在快速入门的案例中新增加一个功能,进入首页后,默认跳转到/about路径下

[图片上传失败...(image-dd0f42-1651062725158)]

[图片上传失败...(image-3e5d75-1651062725158)]

Redirect总结:

Redirect的使用

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

2.具体编码:
<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
  <Redirect to="/about"/>
</Switch>

三、解决样式丢失问题

样式丢失:

  • BrowserRouter中存在的问题:相对路径导致,多级路径刷新页面样式丢失。
  • 样式丢失的原因:混入了路径,一刷新,就找不到对应的样式css文件了

解决方法:

1、改变index.html中引入bootstap的路径

原本: <link rel="stylesheet" href="./css/bootstrap.css">

删去点:<link rel="stylesheet" href="/css/bootstrap.css">

原因:因为加了点的话,就是相对路径,就是从当前文件出发来查找,所以就从localhost:3000/gogocj文件路径出发来找就错了

如果不写点的话,就是直接在 localhost:3000/ 中找

2、修改引入link路径( %PUBLIC_URL% 这是适用于在React中写,指的就是public目录,相当于写死了)

<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">

这种就是绝对路径了,更加不可能有问题了,因为 %PUBLIC_URL% 表示的就是 localhost:3000

3、不改变link的路径,<link rel="stylesheet" href="./css/bootstrap.css">把BrowserRouter改成HashRouter

原因:hash路由中会有一个#,#号后面的东西都代表是前端的东西,是不会像后端请求的

然后一刷新的时候,都是直接忽略#号后面的东西了

也就是:我们在 localhost:3000/#/gogocj/home 刷新页面的时候,会把#省略,直接给localhost:3000/发送请求

总结:

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

1.public/index.html 中 引入样式时不写 ./ 写 / (常用)

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

3.使用HashRouter

总结:前两种都是改变link的路径,第三种是直接修改了路由的模式。

四、路由的模糊匹配和严格匹配

我们一般使用react路由时,链接要与组件相匹配,如下使用

<NavLink to='/home'>Home</NavLink>
 
<Route path='/home' component={Home}/>

1. 模糊匹配

如下所示,当链接第一级路径能匹配上相应的组件,而链接后面再多几级路径也不会产生影响,依旧能正常匹配(当然链接路径少于组件路径时不能匹配,像to='/a/home/b'这样也无法匹配)

<NavLink to='/home/a/b'>Home</NavLink>


<Route path='/home' component={Home}/>  //可以匹配
<Route path='/home/a/b/c' component={Home}/>  //不可以匹配
<Route path='/a/home/b' component={Home}/>  //不可以匹配

2. 严格匹配

有时我们不希望它进行模糊匹配,那么需要在Route上加一个属性exact,即严格的。这样模糊匹配将无法再正常匹配

<NavLink to='/home/a/b'>Home</NavLink>
 
<Route exact path='/home' component={Home}/>//不可以匹配
<Route exact path='/home/a/b' component={Home}/>//可以匹配  链接路径和组件路径,必须一模一样

总结:

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

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

2.开启严格匹配:`<Route exact={true} path="/about" component={About}/>`

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

五、嵌套路由

1. 嵌套子路由

假如我们要给Home组件注册两个子组件路由的时候,

文件建立方式:

因为我们做的News和Message组件其实都是Home组件的子组件的,所以有两种文件建立的模式

(1)在Home文件夹下直接再建立文件夹

(2)在和Home同目录下,建立名为 Home_news的文件夹

路由的匹配

  • 路由是有先后的注册顺序的,会先从最先注册的路由开始查询的

    所以我们子组件嵌套路由,中的 to属性要做前面添加父组件的路径,比如:

 <MyNavLink to="/home/news">News</MyNavLink>
 //原因:由于模糊匹配的原则,前面的home会匹配到父亲组件,所以跳转到的路径就 localhost:3000/home/news 了
  • 这里就是为什么如果有子嵌套路由的话,就不要开始严格匹配的原因
<Switch>
    <Route  path="/about" component={About}/>
    <Route  path="/home" component={Home}/>
    <Redirect to="/about"/>
</Switch>

因为 /home/news 路由一开始是从这三个最先注册的路由开始匹配的,由于模糊匹配所以匹配上了Home,之后才到Home里面再进行子路由的匹配的

如果是严格匹配的话,那么就是直接<Redirect to="/about"/>,直接就到了About组件了,所以就是从外层就屏蔽掉了,根本就进不去子路由了

[图片上传失败...(image-3302a7-1651062725158)]

总结:

嵌套路由

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

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

六、路由组件传参

常见的组件路由传递参数的方式有三种,分别是通过params参数传递利用search属性传递利用state属性传递

1. 传递params参数

[图片上传失败...(image-34b219-1651062725158)]

为什么通过props可以获取到参数呢?我们不妨打印一下this.props看一下此时的Details 组件到底接收了哪些信息。
我们可以看到,此时组件的props属性中,有着三个属性,分别是historylocation和match,而match`属性中已经帮我们把传递的参数封装成为一个对象了。

[图片上传失败...(image-fdd407-1651062725158)]

2. 传递search参数

  • 这种方式基本上和我们常见的在url地址上进行参数拼接是一样的,
  • 这种方式写法较为简单,只需要在 Link 标签中定义好key=value形式参数拼接,
  • 然后在实际的组件中,直接从prop属性中获取,调用querystring(或者其他处理字符串转对象的函数库)处理参数,并封装成对象返回即可

[图片上传失败...(image-535089-1651062725158)]

注意:

  • 这种方式下的传参并不需要额外对路由进行配置
  • props.location.search属性中把我们Link的参数都封装了进去,但是这种方式封装的参数是字符串形式的,我们并不能直接使用,而是要借助queryString库来帮助我们把字符串解析成对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DszxSfhy-1651047151071)(C:\Users\ACER\AppData\Roaming\Typora\typora-user-images\image-20220424132909094.png)]

3. 传递state参数

  • 和前两种传递参数的方式不同,这种方式传递的参数是不会在浏览器中显式的展示的,
  • 同时,即使刷新页面,路由子页面的数据还是不会消失。
  • 原因是react路由器帮我们对维护了history对象(history对象中又维护了location对象,所以也就有了state对象)

[图片上传失败...(image-94f6a4-1651062725158)]

注意:

  • 这种方式下的传参并不需要额外对路由进行配置
  • location.state属性中,我们可以获取到我们在Link中传递的参数。

[图片上传失败...(image-229b39-1651062725158)]

总结

向路由组件传递参数

1.params参数

   路由链接(携带参数):`<Link to='/demo/test/tom/18'}>详情</Link>`

   注册路由(声明接收):`<Route path="/demo/test/:name/:age" component={Test}/>`

   接收参数:this.props.match.params

2.search参数

   路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>

   注册路由(无需声明,正常注册即可):`<Route path="/demo/test" component={Test}/>`

   接收参数:this.props.location.search

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

3.state参数

   路由链接(携带参数):`<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>`

   注册路由(无需声明,正常注册即可):`<Route path="/demo/test" component={Test}/>`

   接收参数:`this.props.location.state`

   备注:刷新也可以保留住参数

七、多种路由跳转方式

1. 路由跳转的两种模式 push与replace

 push: a-b-c 跳转会形成history,可返回到上一层
 //push模式是栈的常规模式 跳转操作进入入栈
 // 例: this.props.history.push('路由地址')
 replace: a-b-c 跳转不会形成history,不可返回到上一层 适用于登录后,不需要重新回到登页面
 //replace模式是替换模式,会替换掉栈顶的路由
 //例: this.props.history.replace('路由地址')

2. 编程式路由导航

除了使用路由组件进行跳转之外,其实我们自己也可以利用事件处理函数来走路由跳转和参数传递的功能。

go & goBack & goForward

在路由组件中,我们可以通过this.prop.history获取到history对象,然后使用对象对应的API实现(历史)路径的跳转。简单理解的话,就是通过路由组件的history对象,实现页面前进、后退的功能。

[图片上传失败...(image-3edc0b-1651062725158)]

总结:

编程式路由导航

 借助this.prosp.history对象上的API对操作路由跳转、前进、后退

   -this.prosp.history.push()

   -this.prosp.history.replace()

   -this.prosp.history.goBack()

   -this.prosp.history.goForward()

   -this.prosp.history.go()

3.通过history对象主动调用push/replace方法

我们可以在路由组件中通过this.props.history获取到history对象后,通过主动调用push或者replace方法来进行路由的跳转,需要注意的是,当通过编程式事务主动进行路由跳转时,对应的参数需要和之前一样,根据传递方式的不同来定义

传递参数格式根据传递方式的不同来定义:

[图片上传失败...(image-46603a-1651062725158)]

八、withRouter的使用

高阶组件中的withRouter, 作用是将一个组件包裹进Route里面, 然后react-router的三个对象history, location, match就会被放进这个组件的props属性中.

// withRouter实现原理: 
// 将组件包裹进 Route, 然后返回
// const withRouter = () => {
//     return () => {
//         return <Route component={Nav} />
//     }
// }

// 这里是简化版
const withRouter = ( Component ) => () => <Route component={ Component }/>
//withRouter的返回值是一个新组件

如果我们某个东西不是一个Router, 但是我们要依靠它去进行浏览记录的前进后退 这时候就可以使用withRouter,将一般组件变成路由组件

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom' //引入withRouter

class Header extends Component {

    back = ()=>{
        this.props.history.goBack()
    }

    forward = ()=>{
        this.props.history.goForward()
    }

    go = ()=>{
        this.props.history.go(-2)
    }

    render() {
        console.log('Header组件收到的props是',this.props);
        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)

//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件

九、前端路由的区别

BrowserRouter与HashRouter的区别

1.底层原理不一样:

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

  HashRouter使用的是URL的哈希值。

2.path表现形式不一样

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

  HashRouter的路径包含#,例如:localhost:3000/#/demo/test

3.刷新后对路由state参数的影响

  (1).BrowserRouter没有任何影响,因为state保存在history对象中。

  (2).HashRouter刷新后会导致路由state参数的丢失!!!

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348