需求:
1、点击左侧导航选项,展示对应的组件内容
2、点击Home组件的菜单,显示二级路由的组件
3、点击Message下的列表,显示三级路由的组件
4、三级路由的组件同属一个,根据路由传参显示不同内容
5、点击push查看,使用编程式实现push模式跳转
6、点击replace查看,使用编程式实现replace模式跳转
5.1 基本路由使用
5.1.1 路由的基本使用
概要总结
1、安装react-router-dom
2、创建主组件以及两个内容组件
3、创建路由链接
4、注册路由
5、全局使用同一个路由器
一、安装react-router-dom
npm i react-router-dom@5
注意:在2021年11月份升级到了6版本,可以在ReactRouter6教程详细讲解。
二、创建主组件以及两个内容组件
主组件App.js:
import React, {Component} from 'react';
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
??????
</div>
</div>
</div>
</div>
</div>
);
}
}
内容组件About.jsx:
import React, {Component} from 'react';
export default class About extends Component {
render() {
return (
<h3>我是About的内容</h3>
);
}
}
内容组件Home.jsx:
import React, {Component} from 'react';
export default class Home extends Component {
render() {
return (
<h3>我是Home的内容</h3>
);
}
}
三、创建路由链接
1、引入标签
注意:to里的链接不要写成大写,例如/About应该写成/about。路径不要带上.,例如./about应该写成/about。
import {Link} from 'react-router-dom'
<div className="list-group">
{/* 原生html中,靠<a>跳转不同的页面 */}
{/*<a className="list-group-item" href="./about.html">About</a>*/}
{/*<a className="list-group-item active" href="./home.html">Home</a>*/}
{/* 在React中靠路由链接实现切换组件 */}
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>
2、引用标签
单单的使用<Link>标签替换了<a>标签实现路由链接,它会报错:你不应该在<Router>标签的外侧使用<Link>。
路由链接也好,路由也好,都得受到路由器的管理,它的意思是希望你在<Link>标签的外层包一层<Router>
import {Link, Router} from 'react-router-dom'
<div className="list-group">
{/* 在React中靠路由链接实现切换组件 */}
<Router>
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</Router>
</div>
3、引用<BrowserRouter>标签
使用了<Router>标签包裹<Link>之后,它又会报另一个错误:它不能在undefined上读取location属性。而且它报的是Router内置库的错误。
这里报错不够精细,因为Router分为两种:BrowserRouter和HashRouter,一个是history一个是哈希。所以<Router>标签还是不行,因为它不知道你到底是哪一种路由器,可以选择<BrowserRouter>
import {Link, BrowserRouter} from 'react-router-dom'
<div className="list-group">
{/* 在React中靠路由链接实现切换组件 */}
<BrowserRouter>
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</BrowserRouter>
</div>
四、注册路由
1、引入<Route>标签
import {Link, BrowserRouter, Route} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
2、引用<BrowserRouter>标签
单单的使用<Route>标签实现路由注册,它会报错:你不应该在标签的外侧使用<Route>。
同理,使用<BrowserRouter>标签在外层包裹。
import {Link, BrowserRouter, Route} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<BrowserRouter>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</BrowserRouter>
</div>
五、全局使用同一个路由器
一个页面只能允许出现一个路由器,但是这里已经出现了两个<BrowserRouter>,一个在路由链接,一个在注册路由。所以在这种情况下是不能相互监测的。
最佳的办法就是在index.js里,直接在<App>根组件外面包一层<BrowserRouter>路由器,那无论里面有什么内容,绝对保证使用的是同一个路由器。
index.js:
import {BrowserRouter} from 'react-router-dom';
// 引入App组件
import App from './App'
const containter = document.getElementById('root')
const root = createRoot(containter)
// 渲染App到页面
root.render(
<BrowserRouter>
<App/>
</BrowserRouter>
)
5.1.2 路由组件与一般组件
概要总结
1、路由组件
2、一般组件
3、路由组件与一般组件的区别
一、路由组件
1、路由组件指的是由路由注册的组件
2、路由组件放在pages目录下
二、一般组件
1、一般组件指的是由自己import引入的组件
2、一般组件放在components目录下
三、路由组件与一般组件的区别
这两种组件最大的区别在于props。
1、一般组件props
一般组件的props基于父组件的传值,父组件传什么props就是什么。
<Header a={1}/>
import React, {Component} from 'react';
export default class Header extends Component {
render() {
console.log('Header组件收到的props是', this.props)
return (
<div className="page-header">
<h2>React Router Demo</h2>
</div>
);
}
}
2、路由组件props
路由组件的props,就算没有传值,它的props也会默认接收到一些来自路由的参数。
<Link className="list-group-item" to="/home">Home</Link>
import React, {Component} from 'react';
export default class About extends Component {
render() {
console.log('About组件收到的props是', this.props)
return ( <h3>我是About的内容</h3> );
}
}
5.1.3 NavLink的使用
概要总结
1、NavLink的使用
2、使用activeClassName自定义类名
一、NavLink的使用场景
一般来说,在当前路由的链接需要加一个class类高亮显示。这时候可以使用来代替,因为它可以在当前路由下默认添加一个active类,也可以添加指定的class类名。
1、默认添加active类名
import {NavLink, Route} from 'react-router-dom'
<div className="list-group">
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<NavLink className="list-group-item" to="/about">About</NavLink>
<NavLink className="list-group-item" to="/home">Home</NavLink>
</div>
2、使用activeClassName自定义类名
index.html:
<style>
.atguigu {
background-color: rgb(209, 137, 4) !important;
color: white !important;
}
</style>
App.js:
<div className="list-group">
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="atguigu" className="list-group-item" to="/home">Home</NavLink> </div>
5.1.4 封装NavLink组件
概要总结
1、封装个NavLink组件
2、解决传参个数过多问题
3、使用children属性显示标签体内容
一、封装NavLink组件
对于NavLink我们可以进行二次封装,把固定的参数写在组件里,动态的通过传参方式传入组件内。
App.js:
<div className="list-group">
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<MyNavLink to="/about"/>
<MyNavLink to="/home"/>
</div>
MyNavLink.jsx:
import React, {Component} from 'react';
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
const {to} = this.props
return (
<NavLink activeClassName="atguigu" className="list-group-item" to={to}>About</NavLink>
);
}
}
二、解决传参个数过多问题
如果参数过多,而且这些参数都是在<NavLink>标签上所需要用的,那么在组件接收的时候,需要一个一个参数通过props拿下来,然后再一个一个地写到<NavLink>里:
App.js:
<MyNavLink to="/about" title="About" a={1} b={2} c={3}/>
MyNavLink.jsx:
import React, {Component} from 'react';
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
const {to, title, a, b, c} = this.props
return (
<NavLink activeClassName="atguigu" className="list-group-item" a={a} b={b} c={c} to={to}>{title}</NavLink>
);
}
}
在React中,可以通过展开运算符,把对象展开然后传给组件即可,例如<NavLink {...this.props}/>
import React, {Component} from 'react';
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
const {title} = this.props
return (
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}>{title}</NavLink>
);
}
}
三、标签体内容传参
1、使用children接参
对于标签属性可以通过props传递,其实标签体内容也是一个特殊的标签属性。
<MyNavLink to="/about" a={1} b={2} c={3}>About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
react帮我们把标签体内容用children作为key传递过去了。
2、优化子组件标签体
对于子组件而言,父组件通过children传递了标签体内容,那么子组件的标签体就应该使用this.props.children来显示:
import React, {Component} from 'react';
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
console.log(this.props)
return (
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}>{this.props.children}</NavLink>
);
}
}
这里有一个简写的形式,就是标签体可以不写,只要组件存在children属性,标签体内容同样能够显示:
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props} />
5.1.5 Switch的使用
概要总结
1、注册重复路由
2、Switch标签的使用
一、注册重复路由
如果在注册路由的时候,注册了两个相同的路径,但匹配不同的组件。这个时候react会把两个组件全都渲染上去,例如:
App.js:
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/home" component={Test} />
</div>
这种情况的出现显然是不合理的,正常情况下肯定是一个路径对应一个组件,如果需要匹配多个组件,那就应该把各个组件组装在一起。
二、Switch标签
按照react路由匹配的原理,它是由上往下匹配的,而且就算匹配到了还会继续往下匹配,所以它才会出现同一个路由匹配了多个组件的情况。
这样会产生一个问题,如果注册的路由非常多,而每个路由它又是全部遍历,性能就会特别差,而且也不符合匹配原则。Switch标签的作用是只要路由一旦匹配成功,它就跳出去不再往下匹配:
App.js:
import {Route, Switch} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/home" component={Test} />
</Switch>
</div>
5.1.6 解决样式丢失问题
概要总结
1、多层级路由产生样式加载路径错误
2、三种样式路径错误解决方案
(1)绝对路径
(2)%PUBLIC_URL%
(3)使用哈希路由HashRouter
一、样式加载路径出错
如果在注册路由的时候,路径是多层级的,例如/atguigu/about,这样刷新页面的时候,样式加载的路径会发生以下的错误:
App.js:
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/atguigu/about" component={About} />
<Route path="/atguigu/home" component={Home} />
</Switch>
</div>
react里只要你请求的资源不存在,它就会把public里的index.html返回给你:
二、解决样式路径错误方案
1、绝对路径
在index.html引入样式的时候,使用的是绝对路径/,不要用相对路径./。
<link rel="stylesheet" href="/css/bootstrap.css">
2、%PUBLIC_URL%
%PUBLIC_URL%是react内定的一个指向public目录的标识符。
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
3、使用哈希路由HashRouter
HashRouter与BrowserRouter的区别就在于url路径是否有#,#号后面的都认为是前端的资源,不会带给服务器。所以在读取资源的时候,它会自动忽略#后面的路径,只会拿到前面的ip端口。
// 引入ReactDOM的createRoot
import {createRoot} from 'react-dom/client'
// 引入react-router-dom库
import {HashRouter} from 'react-router-dom';
// 引入App组件 import App from './App'
const containter = document.getElementById('root')
const root = createRoot(containter)
// 渲染App到页面
root.render(
<HashRouter>
<App/>
</HashRouter>
)
5.1.7 路由的模糊匹配与严格匹配
概要总结
1、模糊匹配
2、严格匹配
一、模糊匹配
当路由链接的to与注册路由的path不一致的时候,要分两种情况。
1、to的链接包含path
如果to的链接比注册路由的path还要长,路由是可以匹配成功的。
<div className="list-group">
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home/a/b">Home</MyNavLink>
</div>
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</Switch>
</div>
2、path的路径包含to
如果注册路由的path比to要长,这样是匹配不成功的,react要求to的路由链接最起码要满足path的长度。
<div className="list-group">
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
</div>
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home/a/b" component={Home} />
</Switch>
</div>
总结:路由需要匹配的path,可以给多但不能给少,而且是左往右匹配,如果不是按顺序,即使包含也不能匹配,例如/a/home/b无法匹配/home:
<div className="list-group">
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/a/home/b">Home</MyNavLink>
</div>
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</Switch>
</div>
二、严格匹配
严格匹配就是to和path不多不少,完全一致。可以通过在里添加exact设置为true即可。
注意:严格匹配模式不要随便开启,开启了有时候会导致无法继续匹配二级路由,除非是路由跳转出问题可以考虑使用。
<div className="list-group">
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home/a/b">Home</MyNavLink>
</div>
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route exact path="/about" component={About} />
<Route exact path="/home" component={Home} />
</Switch>
</div>
5.1.8 Redirect的使用
概要总结
1、Redirect的使用
一、Redirect的使用
当路由在所有注册路由都没有匹配上的时候,可以使用Redirect标签跳转到指定的路由。它通常用在一个默认的初始化路由。
通常我们会指定每一个页面对应的路由,但一般不会指定根目录的路由,此时可以通过Redirect标签去重定向到其它的路由:
import {Route, Switch, Redirect} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Redirect to="/about"/>
</Switch>
</div>
注意:Redirect标签一般配置在所有路由的最末尾的地方。
5.2 嵌套路由使用
5.2.1 嵌套路由
概要总结
1、创建两个二级路由组件
2、一级路由组件注册二级路由
3、路由匹配原则
一、创建两个二级路由组件
Home/News/index.jsx:
import React, {Component} from 'react';
export default class News extends Component {
render() {
return (
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
);
}
}
Home/Message/index.jsx:
import React, {Component} from 'react';
export default class Message extends Component {
render() {
return (
<ul>
<li><a href="/message1">message001</a></li>
<li> <a href="/message2">message002</a></li>
<li> <a href="/message/3">message003</a></li>
</ul>
);
}
}
二、一级路由组件注册二级路由
Home/index.jsx:
import React, {Component} from 'react';
import {Route, Switch, Redirect} from 'react-router-dom'
import MyNavLink from '../../components/MyNavLink'
import News from './News'
import Message from './Message'
export default class Home extends Component {
render() {
return (
<div>
<div>
<h3>我是Home的内容</h3>
<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}/>
<Redirect to="/home/news"/>
</Switch>
</div>
</div>
);
}
}
三、路由匹配原则
1、路由注册有先后顺序
一级路由app.js:
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Redirect to="/about"/>
</Switch>
二级路由Home/index.jsx:
<Switch>
<Route path="/home/news" component={News}/>
<Route path="/home/message" component={Message}/>
<Redirect to="/home/news"/>
</Switch>
About与Home组件是属于一级路由组件,News与Message组件属于Home组件的二级路由组件。在一级路由中,路由路径/home对应的是Home组件,在二级路由自身的路径是/news和/message,但是前面要把一级路由路径/home也带上。
这是因为在二级路由中,访问的路径假设是/home/message,那么react会从一级路由到二级路由逐个逐个去匹配。首先匹配到一级路由的/home,这个是匹配成功的,所以Home组件渲染出来。然后由于Home组件里也注册了路由,因此继续匹配到二级路由的/home/message,Message组件也渲染出来。
如果二级路由的message路径并没有带上一级路径/home,而是直接写/message,那么根据路由匹配的先后顺序原则,在第一层路由就只有/about和/home,否则就去Redirect重定向到/about:
<div>
<h3>我是Home的内容</h3>
<ul className="nav nav-tabs">
<li>
<MyNavLink to="/news">News</MyNavLink>
</li>
<li>
<MyNavLink to="/message">Message</MyNavLink>
</li>
</ul>
</div>
2、路由的严格模式
一旦开启了路由的严格模式,那就意味着路由路径必须精准匹配,但如果在一级路由就开启严格模式,那么会直接导致二级路由无法匹配。
因为二级路由假设/home/message,它来到一级路由匹配的时候,发现/home是开启了严格模式,因此匹配失败了,直接Redirect到/about。一级路由匹配失败,那二级路由就更不用匹配了。
<Switch>
<Route path="/about" component={About} />
<Route exact path="/home" component={Home} />
<Redirect to="/about"/>
</Switch>
5.3 向路由组件传递参数数据
5.3.1 向路由组件传递params参数
概要总结
1、创建一个三极路由组件
2、二级路由组件注册三级路由
3、路由组件传递params参数
一、创建一个三级路由组件
Home/Message/Detail/index.jsx:
import React, {Component} from 'react'
export default class Index extends Component {
render() {
return (
<ul>
<li>ID:???</li>
<li>TITLE:???</li>
<li>CONTENT:???</li>
</ul>
);
}
}
二、二级路由组件注册三级路由
Home/message/index.jsx:
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(msgObj => {
return (
<li key={msgObj.id}>
<Link to="/home/message/detail">{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr />
<Route path="/home/message/detail" component={Detail} />
</div>
);
}
}
三、路由组件传递params参数
1、路由链接传递params参数
传递params参数,其实就是把参数拼在路径的后面,类似于ajax的params参数。
Home/message/index.jsx:
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
<Link to={`/home/message/detail/${msgObj.id}`}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr />
<Route path="/home/message/detail" component={Detail} />
2、注册路由声明变量接收参数
这样子虽然已经把参数拼在了路径的后面,但这对于路由来说只是模糊匹配,组件内部是无法接受到的,因此在注册路由中还需要声明接收params参数:
{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id" component={Detail} />
如果需要传递多个参数,那就声明多个变量来接收:
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
{/* 向路由组件传递params参数 */}
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr />
{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail} />
声明了变量接收参数之后,在路由组件内部就可以通过props查看参数了,它就存放在match里的params中:
5.3.2 向路由组件传递search参数
概要总结
1、路由组件传递search参数
2、querystring库的使用
3、使用querystring库解析search参数
一、路由组件传递search参数
1、路由链接传递search参数
传递search参数,其实就是把参数通过?和&以键值对的方式拼在路径的后面,类似于ajax的query参数。
Home/message/index.jsx:
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
{/* 向路由组件传递search参数 */}
<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
2、search参数无需声明接收
接收search参数与接收params参数不同,接收params参数在注册路由的时候是需要声明变量对应接收的,而search参数由于已经把字段和值全部写在了路径上,因此它不用再声明了,跟普通注册路由一样即可。
{/* search参数无需声明接收 */}
<Route path="/home/message/detail" component={Detail} />
声明了变量接收参数之后,在路由组件内部就可以通过props查看参数了,它就存放在location里的search中:
二、querystring库的使用
1、安装querystring依赖
npm i querystring
2、将对象转换成urlencoded编码
多种key=value用&分隔的编码形式叫做urlencoded编码。通过querystring的stringify方法把对象转换成urlencoded编码。
let obj = {name: 'tom', age: 18}
console.log(qs.stringify(obj)) // name=tom&age=18 key=value&key=value
3、将urlencoded编码转换成对象
通过querystring的parse方法把urlencoded编码转换成对象。
let str = 'carName=奔驰&price=199'
console.log(qs.parse(str)) // {carName: '奔驰', price: '199'}
三、使用querystring库解析search参数
我们拿到search参数之后,截取掉第一个?字符,实际上它是一个urlencoded编码格式,还需要进一步转换成对象。我们可以通过querystring库的parse方法进行解析。
render() {
// 接收search参数
const {search} = this.props.location // ?id=01&title=消息1
const {id, title} = qs.parse(search.slice(1)) // {id: '01', title: '消息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>
);
}
5.3.3 向路由组件传递state参数
概要总结
1、路由组件传递state参数
2、state参数具备缓存作用
一、路由组件传递state参数
1、路由链接传递state参数
无论是params参数还是search参数,都会暴露在url栏地址上。而state参数是隐藏的,不会暴露在url上。
传递state参数,或者的to不能直接传地址,而是要传一个对象,对象里的pathname代表路由路径,而state是传参的键值对。
Home/message/index.jsx:
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
{/* 向路由组件传递state参数 */}
<Link to={{pathname: '/home/message/detail', state: {id: msgObj.id, title: msgObj.title}}}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
2、state参数无需声明接收
接收state参数与接收search参数一样,不需要声明变量来接收,跟普通注册路由一样即可。
{/* state参数无需声明接收 */}
<Route path="/home/message/detail" component={Detail} />
声明了变量接收参数之后,在路由组件内部就可以通过props查看参数了,它就存放在location里的state中:
二、state参数具备缓存作用
相比params和search两种参数形式,state是惟一一种不在url路径暴露参数的形式,但是它在刷新的时候,它依然知道上一次的记录。
由于我们用的是BrowserRouter,它一直在操作浏览器的history,所以state参数虽然没有暴露参数,但是history是有记录的。在props里的location的state参数,实际上它同样存在于props的history/location/state,location是history的子属性而已。
如果此时把浏览器缓存清空,这样所有的历史记录都删掉,那么state当然也就不存在了:
因此在使用state参数的时候,要注意处理第一次history没有记录的时候的兼容。
5.3.4 总结路由参数
概要总结
1、路由参数总结
一、向路由组件传递参数
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/:name/:age" component={Test}/>
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3、state参数
路由链接(携带参数):<Linkto={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo/test/:name/:age" component={Test}/>
接收参数:this.props.location.state
备注:刷新也可以保留住参数
5.4 多种路由跳转方式
5.4.1 push与replace
概要总结
1、push与replace
一、push与replace
路由的工作原理,其实就是对浏览器的历史记录进行操作,一共是两种:push和replace。push是一个压栈的操作,会留下痕迹。而replace会替换掉当前的记录,不会留下痕迹。
路由它默认的是push操作,如下图所示:
如果需要开启replace模式,其实只需要在加一个replace即可:
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
{/* 向路由组件传递state参数 */}
<Link replace to={{pathname: '/home/message/detail', state: {id: msgObj.id, title: msgObj.title}}}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
注意:开启了replace模式,也就相当于开启了无痕模式。
5.4.2 编程式路由导航
概要总结
1、编程式路由导航
2、路由的前进与后退
一、编程式路由导航
编程式路由导航,简单来说就是不借助或者,而是通过代码来实现路由跳转。
react路由组件里,props的history提供了push和replace两个方法分别实现编程式的push模式和replace模式,即this.props.history.push()和this.props.history.replace()。
以下是push和replace模式的params、search、state三种模式的路由方法:
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}) }
在注册路由的时候,注意分别对应这3种模式来注册即可:
{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail} />
{/* search参数无需声明接收 */}
{/*<Route path="/home/message/detail" component={Detail} />*/}
{/* state参数无需声明接收 */}
{/*<Route path="/home/message/detail" component={Detail} />*/}
二、路由的前进与后退
react路由组件里,props的history还提供了go、goBack和goForward三个方法,用于控制路由的前进和后退。goBack方法回退一步,goForward前进一步,go方法通过传参控制,1代表前进一步,-1代表后退一步,以此类推。
back = () => {
this.props.history.goBack()
}
forward = () => {
this.props.history.goForward()
}
go = () => {
this.props.history.go(-2)
}
5.4.3 withRouter的使用
概要总结
1、withRouter的使用
一、withRouter的使用
通过路由注册的组件称之为路由组件,这些组件react会通过props传递一系列的路由参数,例如history、location、match等等。
对于一般组件来说,props是不会有这些路由参数的。
如果一般组件也想使用路由的方法,可以借助于withRouter。withRouter可以接收一个组件,它的作用是给一般组件加上路由的属性。
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom'
class Header extends Component {
render() {
console.log('Header组件收到的props是', this.props)
return ( ...... );
}
}
export default withRouter(Header)
5.4.4 BrowserRouter与HashRouter
概要总结
1、BrowserRouter对比HashRouter
一、底层原理不一样
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本
HashRouter使用的是URL的哈希值。
二、url表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径中包含#,例如:localhost:3000/#/demo/test
三、刷新后对路由state参数的影响
BrowserRouter没有任何影响,因为state保存在history对象中
HashRouter刷新后会导致路由state参数的丢失
备注:HashRouter可以用于解决一些路径错误相关的问题。