目录
现代的前端应用大多都是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。为了有效的使用单个页面来管理原来多页面的功能,前端路由应运而生。
对SPA的理解:
单页Web应用(single page web application,SPA)。整个应用只有一个完整的页面。点击页面中的链接不会刷新页面,只会做页面的局部更新。数据都需要通过ajax请求获取,并在前端异步展现。
React路由
路由分为两种,一种是后端路由:用来处理客户端提交的请求,用node举例,当node接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求返回响应数据;另一种是前端路由:用于展示页面内容,当浏览器的path变为/test时,当前路由组件就会变成Test组件。
前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面)
前端路由是一套映射规则,在React中,是URL路径与组件的对应关系
使用React路由简单来说,就是配置 路径 和 组件 (配对)
react-router-dom的安装与使用
react-router是React官方维护的路由库,其主要有三种应用场景:web、native、any,顾名思义分别是web端、native端和任何场景都能使用的any,作为web前端开发人员,选择web、any都是可以的,但是web的端的针对性更强,所以推荐选择web,其第三方包名为:react-router-dom,与我们要操作浏览器的元素的想法不谋而合。
其官方网址:官方网址 ,因为React是外国人开发的框架,所以其网址内容都是英文,所以还是推荐一下中文文档:中文文档 。
注意:由于react-router-dom在2021年11月份升级到了6版本,此处讲解的是react-router-dom的5版本,关于最新的6版本会在下次文章中讲解,想要了解的可以订阅一下本专栏,博主将持续分享React知识:React专栏 。
终端执行如下命令进行安装 react-router-dom (5版本):
npm install react-router-dom@5
明确好界面中的导航区与展示区,导航区的a标签改为Link标签,展示区写Route标签进行路径匹配。
// 导航区
<Link to="/xxxxx">Demo</Link>
// 展示区
<Route path="/xxxx" component={Demo}>
import React, { Component } from 'react'
import {Link,Route} from "react-router-dom"
import Home from './components/Home/Home.jsx'
import About from './components/About/About.jsx'
export default class App extends Component {
render() {
return (
<div>
<div className='row ml-2'>
<div className="col-xs-offset-2 col-xs-8">
<div className='page-header'><h2>React Router Demo</h2></div>
</div>
</div>
<div className='row ml-2'>
<div className=" col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 导航区 */}
<Link className="list-group-item" to="/home">Home</Link>
<Link className="list-group-item" to="/about">About</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="pandel-body">
{/* 展示区 */}
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
</div>
</div>
</div>
</div>
</div>
)
}
}
<App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>,因为HashRouter的url地址上会一直出现 # 看着比较不舒服,所以BrowserRouter是比较符合人们的要求的,当然这里看个人选择
import React from 'react';
import ReactDOM from "react-dom";
import {BrowserRouter} from 'react-router-dom'
import App from './App'
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
,document.getElementById('root'))
路由组件与一般组件的区别
写法不同:
路由组件:<Route path=”/xxxx” component={Demo}>
一般组件:<Demo/>
存放位置不同:
路由组件:pages
一般组件:components
接收到的props不同:
路由组件:接收到三个固定属性,分别是:history、location、match
一般组件:组件标签传递了什么就能收到什么
BrowserRouter和HashRouter的区别
底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。HashRouter使用的是URL的哈希值。
path表现形式不一样:
BrowserRouter的路径中没有#,例如: localhost:3000/demo/test,HashRouter的路径包含#,例如: localhost:3000/#/demo/test。
刷新后对路由state参数的影响:
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失! ! !
备注: HashRouter可以用于解决一些路径错误相关的问题。
与普通的导航区标签Link不同,NavLink会给自己的标签添加一个激活的class属性,只要有点击激活该链接,class属性名就会出现,class属性名默认是 active ,可以搭配bootstrop使用,如果想更改class属性名可以使用activeClassName。具体如下:
当然使用NavLink也要先进行导入,导入之后就可以使用了。
为我们设置的class类名添加属性,因为就一个样式没有必要单独写在css文件,这里直接写在我们的index.html的style即可,如下:
我们发现,只要我们要设置NavLink的样式就得重复的写一遍activeClassName,有没有办法只需要写NavLink不同的地方比如to属性呢?答案是有的,需要我们将NavLink封装成一个一般组件,用我们封装好的组件来解决这个问题:
封装的组件内容如下,将相同的内容写在如下NavLink中,不同的内容通过父传子将内容传到自己封装的组件标签中,用展开运算符将所有传来的属性平铺到标签上,包括children属性:
import React, { Component } from 'react'
import {NavLink} from "react-router-dom"
export default class MyNavLink extends Component {
render() {
return (
<NavLink activeClassName='zhangsan' className="list-group-item" {...this.props}/>
)
}
}
总结
NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
标签体内容是一个特殊的属性标签
通过this.props.children可以获取标签体内容
Switch的使用以及模糊与严格匹配
不知道大家有没有考虑过这样一个问题,我们在展示区注册路由时,一个path路径引用多个组件,那么当前这个路径下的页面应该展示什么内容呢?如下:
可以看到当前的about路径下引用的两个组件的内容都出现了:
要知道,一般我们在进行路由注册组件的时候,一个路径匹配一个组件,为了避免这种情况,我们采用Switch进行规避:
使用Switch之后,如果出现多个组件共用一个路径,默认只展示第一个,当然Switch也是要在路由库中导入才能使用:
解决完组件的混乱之后,我们在处理path路径的混乱,我们在注册路由的时候默认是模糊匹配,这是什么情况呢?如下:
我们在路由链接的to属性下,给出了具体的路径,而在展示区时,给出的路径仅仅的一点点,这种是能够达到模糊匹配的条件的,也是能在页面上展示出内容的
处理以上的情况,其它的情况都是不满足模糊匹配的条件的,如下几个例子举出:
严格匹配模式下,路由链接与路由占位符的path路径必须一一对应,出现一点路径问题都会匹配不上,如下:
总结
Switch的使用
通常情况下,path和component是一一对应关系。Switch可以提高路由匹配效率(单一匹配)。
严格匹配与模糊匹配
1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要
致)
⒉.开启严格匹配:<Route exact={true} path=”/about” component={About}/>
3.严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
嵌套路由
嵌套路由很简单,在要嵌套的组件下,在写一份路由的匹配规则即可,如下:
import React, { Component } from 'react'
import MyNavLink from '../../components/MyNavLink/index.jsx'
import { Switch,Route } from 'react-router-dom'
import News from './News'
import Message from './Message'
export default class Home extends Component {
render() {
return (
<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>
</div>
{/* 注册路由——展示区 */}
<Switch>
<Route path="/home/news" component={News} />
<Route path="/home/message" component={Message} />
</Switch>
</div>
)
}
}
如果重定向路由,可以使用Redirect,其一般写在所有路由注册的最下方。当所有路由都无法匹配时,跳转到Redirect指定的路由。
总结
注册子路由时要写上父路由的path值,路由的匹配是按照注册路由的顺序进行的。
路由传参
向路由传递参数有以下三种方式:
params参数:
将获取的参数id和title遍历之后依次传入嵌套子组件,路由链接携带参数,注册路由声明接收。
通过解构赋值的办法将传递过来的参数渲染到页面上
match里面就存放着我们传递的数据:
完整代码:
import React, { Component } from 'react'
import { Link,Route } from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr:[
{id:'01',title:'消息1'},
{id:'02',title:'消息2'},
{id:'03',title:'消息3'},
]
}
render() {
const {messageArr} = this.state
return (
<div>
<ul>
{
messageArr.map((messageObj)=>{
return <li key={messageObj.id}>
{/* 向路由组件传递props参数 */}
<Link to={`/home/message/detail/${messageObj.id}/${messageObj.title}`}>{messageObj.title}</Link>
</li>
})
}
</ul>
<hr />
{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail} />
</div>
)
}
}
import React, { Component } from 'react'
const DetailData = [
{id:'01',content:'111'},
{id:'02',content:'222'},
{id:'03',content:'333'},
]
export default class Detail extends Component {
render() {
// 接收params参数
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>
)
}
}
search参数:
search传递参数发送数据十分容易,但是接收到数据有点小繁琐,如下:
我们在传递参数的时候,通过如下类似query参数的形式进行传参,路由声明接收时,无需更改代码,正常注册路由即可,可见search传递参数极为方便 。
但是当我们在子组件去接收props参数时,有点小烦,因为search传递过来的并不是对象的键值对形式,而是一个字符串,需要我们对传递过来的东西进行一定的修改:
不废话直接告诉大家,react本身就有一个qs库,可以帮助我们将类似urlencode的编码格式转成对象格式,如:name=zs&age=18 转成 {name:’zx’,age:18} ,使用方法和json类似。接收的参数是在location里面,获取到的search是urlencoded编码字符串,需要借助qs进行解析:
接下直接使用slice去掉第一个?就可以了,废话不多说,完整代码如下:
import React, { Component } from 'react'
import qs from 'qs'
const DetailData = [
{id:'01',content:'111'},
{id:'02',content:'222'},
{id:'03',content:'333'},
]
export default class Detail extends Component {
render() {
const {search} = this.props.location
// 接收search参数
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>
)
}
}
state参数:
state与我们react中初始化状态的state无关,只是名字相同而已,state传递参数也是最简单的:
state传递参数,路径和参数都放在一个对象里面,state里面存放我们要传递的参数:
接收参数也很简单,一个解构赋值即可,如下:
虽然state传递参数很简单,但是有个问题就是,在我们切换路由的时候,浏览器的url并不会发送任何变化,所以是否使用state还是看个人选择:
总结
这三种形式的传参,项目中都用的很多,都应该尽量掌握,根据具体需求,parmas显示路径相对清爽、search强调参数清楚明了、state隐藏信息安全保障,今后在项目中还是根据大家的需求,具体情况具体方法。
编程式路由导航
浏览器默认情况下是开启push模式的,也就是你在当前页面点击下一个页面,点击浏览器的回退按钮能够回退到你上一次浏览的页面,而开启replace模式后,会替换掉你当前浏览的页面,也就是说,你点击了开启replace模式的网页的话,点击回退会回退到上上个页面,因为上个页面已经被你开启replace模式的网页替换掉了,开启replace方法如下:Link标签添加replace属性即可。
如果想通过按钮的形式来实现编程式导航的跳转,可以调用history:
history是 React路由提供的,用于获取浏览器历史记录的相关信息push(path):
跳转到某个页面,参数path表示要跳转的路径
go(n)∶前进或后退到某个页面,参数n表示前进或后退页面数量(比如:-1表示后退到上一页)
举个简单的例子,给一个定时器,2秒过后,自动跳转到另一个路由:
那么问题来了,如果我想给一般组件也设置一个回退按钮能实现吗?注意:只要路由组件才有其特殊的三个属性的API,一般组件是不能调用路由组件中的history的,怎么办?
withRouter可以加工一般组件,让一般组件具备路由组件所特有的API。
withRouter的返回值是一个新组件。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/139989.html