1、NavLink的使用
一个特殊版本的 Link
,当它与当前 URL
匹配时,为其渲染元素添加样式属性
<NavLink className="list-group-item" to="/home">Home</NavLink>
<NavLink className="list-group-item" to="/about">About</NavLink>
以上代码有一个默认的特殊效果,就是当你选中该路由时会有一个默认的激活样式,默认的类名:active
。
如果你要修改该激活样式,可以自定义样式:
.active1{
background-color: orange !important;
color: white !important;
}
添加一个属性activeClassName
将类名active1
填进去:
<NavLink className="list-group-item" activeClassName="active1" to="/home">Home</NavLink> <NavLink className="list-group-item" activeClassName="active1" to="/about">About</NavLink>
由于这里代码有些冗长,我们简单封装一下:
封装组件MyNavLink
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
return (
<NavLink className="list-group-item" activeClassName="active1" {...this.props}/>
)
}
}
导入组件MyNavLink
import MyNavLink from './components/MyNavLink'
使用组件MyNavLink
<MyNavLink to="/home" children="Home"/>
<MyNavLink to="/about" children="About"/>
将标签属性to
和children
传入组件,其中to
属性的内容就是路由路径,children
就是标签体内容。
小总结
NavLink
可以实现路由链接的高亮,通过activeClassName
指定样式类名- 标签体内容是一个特殊的标签属性
- 通过
this.props.children
可以获取标签体内容
2、Switch的使用
渲染与该地址匹配的第一个子节点<Route>
或者<Redirect>
。
import { Switch, Route } from 'react-router'
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
</Switch>
-
通常情况下
path
与component
是一一对应的关系 -
Switch
可以提高路由匹配的效率(单一匹配)
解决多级路径刷新样式丢失的问题
public/index.html
中 引入样式时不写./
写/
(常用)public/index.html
中 引入样式时不写./
写%PUBLIC_URL%
(常用)- 使用
HashRouter
路由的严格匹配与模糊匹配
- 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配:
<Route exact={true} path="/about" component={About}/>
- 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
3、Redirect组件的使用
页面重定向:一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect
指定的路由
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
详情参见:React路由之Redirect
4、路由嵌套
News组件:
import React, { Component } from 'react'
export default class News extends Component {
render() {
return (
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
)
}
}
Message组件:
import React, { Component } from 'react'
export default class Message extends Component {
render() {
return (
<div>
<ul>
<li>
<a href="/message1">message001</a>
</li>
<li>
<a href="/message2">message002</a>
</li>
<li>
<a href="/message/3">message003</a>
</li>
</ul>
</div>
)
}
}
Home组件:
import React, { Component } from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import MyNavLink from '../MyNavLink'
import News from './News'
import Message from './Message'
export default class Home extends Component {
render() {
// console.log('这是Home组件的Props',this.props)
return (
<div>
<h3>我是Home的内容</h3>
<div>
<ul className="nav nav-tabs">
<li>
{/* 注册子路由时要写上父路由的path值 */}
<MyNavLink to="/home/news">News</MyNavLink>
</li>
<li>
<MyNavLink to="/home/message">Message</MyNavLink>
</li>
</ul>
{/* 注册路由 */}
<Switch>
{/* 注册子路由时要写上父路由的path值 */}
<Route path="/home/news" component={News} />
<Route path="/home/message" component={Message} />
<Redirect to="/home/news" />
</Switch>
</div>
</div>
)
}
}
整体效果:
注意点:
1.注册子路由时要写上父路由的path
值
2.路由的匹配是按照注册路由的顺序进行的
5、向路由组件传递params参数
组件目录结构
src
|--components
| |--Home
| |--News
| |--Message
| |--Detail
| |--About
| |--Header
|--App.jsx
|--index.js
主要组件Message
import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
msgArr: [
{ id: '01', title: 'msg-01' },
{ id: '02', title: 'msg-02' },
{ id: '03', title: 'msg-03' },
],
}
render() {
const { msgArr } = this.state
return (
<div>
<ul>
{msgArr.map((msgObj) => {
return (
<li key={msgObj.id}>
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
</li>
)
})}
</ul>
<hr />
<Route path="/home/message/detail/:id/:title" component={Detail}/>
</div>
)
}
}
URL匹配路径/home/message/:id/:title
时渲染的组件。:id
是一个参数,可以通过props.match.params.id
访问。:title
也是一样。
主要组件Detail
import React, { Component } from 'react'
export default class Detail extends Component {
state = {
info:[
{id:'01',content:'你好我是信息1'},
{id:'02',content:'你好我是信息2'},
{id:'03',content:'你好我是信息3'},
]
}
render() {
console.log(this.props)
const {info} = this.state
const {id,title} = this.props.match.params
const res = info.find(infoObj=>{
return infoObj.id === id
})
return (
<ul>
<li>ID:{id}</li>
<li>TITLE:{title}</li>
<li>CONTENT:{res.content}</li>
</ul>
)
}
}
在这里我们通过解构this.props.match.params
获得参数id
和title
。以下是我们打印的标签属性:
history: Object { length: 44, action: "PUSH", location: {…}, … }
location: Object { pathname: "/home/message/detail/01/msg-01", search: "", key: "5ugqcd", … }
match:
isExact: true
params: Object { id: "01", title: "msg-01" }
id: "01"
title: "msg-01"
path: "/home/message/:id/:title"
url: "/home/message/01/msg-01"
从以上打印结果中,我们可以清晰的看到我们的参数位置:match.params
整体效果
小总结
- 路由链接(携带参数):
<link to='/demo/test/tom/19'>详情</Link>
- 注册路由(声明接收):
<Route path='/demo/test/:name/:age' component={Test} />
- 接收参数:
const {name,age} = this.props.match.params
6、向路由组件传递search参数
修改Message组件
import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
msgArr: [
{ id: '01', title: 'msg-01' },
{ id: '02', title: 'msg-02' },
{ id: '03', title: 'msg-03' },
],
}
render() {
const { msgArr } = this.state
return (
<div>
<ul>
{msgArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 传递params参数 */}
{/* <Link to={`/home/message/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 传递search参数 */}
<Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
</li>
)
})}
</ul>
<hr />
<Route path="/home/message/detail" component={Detail}/>
</div>
)
}
}
这里的路由链接的参数需要使用?key=value&key=value
的格式,也叫urlencode
格式。路由注册照常即可,不需要声明参数。
修改Detail组件
import React, { Component } from 'react'
import qs from 'qs'
export default class Detail extends Component {
state = {
info:[
{id:'01',content:'你好我是信息1'},
{id:'02',content:'你好我是信息2'},
{id:'03',content:'你好我是信息3'},
]
}
render() {
console.log(this.props)
const {info} = this.state
const {search} = this.props.location
const {id,title} = qs.parse(search.slice(1))
const res = info.find(infoObj=>{
return infoObj.id === id
})
return (
<ul>
<li>ID:{id}</li>
<li>TITLE:{title}</li>
<li>CONTENT:{res.content}</li>
</ul>
)
}
}
这里引入了一个库qs
,无需安装,直接引入即可,它有两个方法可以使得urlencode
格式的数据与object
格式的数据进行转换。
qs.stringify()
: 将{a:'1',b:2}
转换>a=1&b=2
qs.parse()
: 将a=1&b=2
转换>{a:"1",b:"2"}
查看标签属性:
history: Object { length: 7, action: "PUSH", location: {…}, … }
location:
hash: ""
key: "0wurd0"
pathname: "/home/message/detail"
search: "?id=01&title=msg-01"
state: undefined
match: Object { path: "/home/message/detail", url: "/home/message/detail/", isExact: true, … }
我们可以看到search
参数是一个字符串在location
对象里面,所以我们只需要将它解构取出值,即可进行后续操作。
小总结
- 路由链接(携带参数):
<link to='/demo/test?name=tom&age=19'>详情</Link>
- 注册路由(无需声明,正常注册即可):
<Route path='/demo/test' component={Test} />
- 接收参数:
const {search} = this.props.location
- 解析参数:获取到的
search
是urlencode
编码字符串,需要借助qs
解析。
7、向路由组件传递state参数
修改Message组件
import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
msgArr: [
{ id: '01', title: 'msg-01' },
{ id: '02', title: 'msg-02' },
{ id: '03', title: 'msg-03' },
],
}
render() {
const { msgArr } = this.state
return (
<div>
<ul>
{msgArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 传递params参数 */}
{/* <Link to={`/home/message/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 传递search参数 */}
{/* <Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 传递state参数 */}
<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
</li>
)
})}
</ul>
<hr />
<Route path="/home/message/detail" component={Detail}/>
</div>
)
}
}
以对象的形式将state
参数放在路由链接里面。
修改的Detail组件
import React, { Component } from 'react'
// import qs from 'qs'
export default class Detail extends Component {
state = {
info:[
{id:'01',content:'你好我是信息1'},
{id:'02',content:'你好我是信息2'},
{id:'03',content:'你好我是信息3'},
]
}
render() {
console.log(this.props)
const {info} = this.state
const {id,title} = this.props.location.state || {}
// const {id,title} = qs.parse(search.slice(1))
const res = info.find(infoObj=>{
return infoObj.id === id
}) || {}
return (
<ul>
<li>ID:{id}</li>
<li>TITLE:{title}</li>
<li>CONTENT:{res.content}</li>
</ul>
)
}
}
获取state
参数也是在location
里面获取,我们可以看看标签属性的结构:
history: Object { length: 11, action: "REPLACE", location: {…}, … }
location:
hash: ""
key: "o47hxa"
pathname: "/home/message/detail"
search: ""
state: Object { id: "01", title: "msg-01" }
match: Object { path: "/home/message/detail", url: "/home/message/detail", isExact: true, … }
我们可以从打印结果里面清晰的看到state
的参数
小总结
- 路由链接(携带参数):
<link to={{pathname:'/demo/test',state:{name:'tom',age:19}}}>详情</Link>
- 注册路由(无需声明,正常注册即可):
<Route path='/demo/test' component={Test} />
- 接收参数:
const {state} = this.props.location
- 解析参数:刷新也可以保留住参数。
8、编程式路由导航
传递params参数
- 注册事件
<button onClick={() => this.pushRoute(msgObj.id, msgObj.title)}>push路由</button>
<button onClick={() => this.replaceRoute(msgObj.id, msgObj.title)}>replace路由</button>
- 事件逻辑
pushRoute = (id, title) => {
// push路由跳转===>params参数
this.props.history.push(`/home/message/detail/${id}/${title}`)
}
replaceRoute = (id, title) => {
// replace路由跳转===>params参数
this.props.history.replace(`/home/message/detail/${id}/${title}`)
}
- 注册路由
<Route path="/home/message/detail/:id/:title" component={Detail} />
- 接收参数
const {id,title} = this.props.match.params
传递search参数
- 注册事件
<button onClick={() => this.pushRoute(msgObj.id, msgObj.title)}>push路由</button>
<button onClick={() => this.replaceRoute(msgObj.id, msgObj.title)}>replace路由</button>
- 事件逻辑
pushRoute = (id, title) => {
// push路由跳转===>search参数
this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
}
replaceRoute = (id, title) => {
// replace路由跳转===>search参数
this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
}
- 注册路由
<Route path="/home/message/detail" component={Detail} />
- 接收参数
const {search} = this.props.location
const {id,title} = qs.parse(search.slice(1))
传递state参数
- 注册事件
<button onClick={() => this.pushRoute(msgObj.id, msgObj.title)}>push路由</button>
<button onClick={() => this.replaceRoute(msgObj.id, msgObj.title)}>replace路由</button>
- 事件逻辑
pushRoute = (id, title) => {
// push路由跳转===>state参数
this.props.history.push('/home/message/detail', { id, title })
}
replaceRoute = (id, title) => {
// replace路由跳转
this.props.history.replace('/home/message/detail', { id, title })
}
- 注册路由
<Route path="/home/message/detail" component={Detail} />
- 接收参数
const {id,title} = this.props.location.state || {}
小总结
编程式路由导航就是借助this.props.history
身上的几个API进行路由的前进、后退、跳转、替换。
this.props.history.go()
this.props.history.goBack()
this.props.history.goForward()
this.props.history.push()
this.props.history.replace()
9、withRouter的使用
我们知道一般组件,如果不传标签属性,接收的props
是一个空对象,是没有那些路由属性和API
的,那么一般组件里面如何使用路由进行跳转呢?那就要使用withRouter
对一般组件进行改造了。
import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'
class Header extends Component {
back = () => {
this.props.history.goBack()
}
forward = () => {
this.props.history.goForward()
}
go1 = () => {
this.props.history.go(-2)
}
render() {
console.log('这是Header组件的Props',this.props)
return (
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header"><h2>React Router Demo</h2></div>
<button onClick={this.back}>后退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.go1}>go</button>
</div>
)
}
}
export default withRouter(Header)
以上是对一般组件Header
进行改造,使得它拥有路由的属性和API
,这样我们就可以使用路由的前进、后退、跳转等API
操作路由了。
小总结
withRouter
可以加工一般组件,使得它拥有路由组件的属性和APIwithRouter
的返回值是一个新的组件
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
参数的影响
(1)BrowserRouter
没有任何影响,因为state
保存在history
对象中
(2)HashRouter
刷新后会导致路由state
参数的丢失
4、备注:HashRouter
可以用于解决一些路径错误相关的问题