06-React的路由
1.相关理解
1).SPA的理解
- 单页Web应用(
single page web application
,SPA)。 - 整个应用只有一个完整的页面。
- 点击页面中的链接不会刷新页面,只会做页面的局部更新。
- 数据都需要通过
ajax
请求获取, 并在前端异步展现。
2).路由的理解
a.什么是路由?
- 一个路由就是一个映射关系(
key:value
) key
为路径,value
可能是function
或component
b.路由分类
①.后端路由:
-
理解:
value
是function
, 用来处理客户端提交的请求。 -
注册路由:
jsrouter.get(path, function(req, res))
-
工作过程:当
node
接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
②.前端路由:
-
浏览器端路由,
value
是component
,用于展示页面内容。 -
注册路由:
jsx<Route path="/test" component={Test}>
-
工作过程:当浏览器的
path
变为/test
时, 当前路由组件就会变为Test
组件
3).react-router-dom的理解
- react的一个插件库。
- 专门用来实现一个SPA应用。
- 基于react的项目基本都会用到此库。
2. react基本路由使用
需求:
1).实现:
a.BrowserRouter,Link,Route三个API的基本使用
-
完成组件的静态结构,明确好界面中的导航区、展示区
jsx//App.jsx export default class App extends Component { render() { return ( <div> <div className="row"> <div className="col-xs-offset-2 col-xs-8"> <Header/> </div> </div> <div className="row"> <div className="col-xs-2 col-xs-offset-2"> <div className="list-group"> {/* 原生html中,靠<a>跳转不同的页面 */} <a className="list-group-item active" href="./about.html">About</a> <a className="list-group-item" href="./home.html">Home</a> </div> </div> <div className="col-xs-6"> <div className="panel"> <div className="panel-body"> <About/> <Home/> </div> </div> </div> </div> </div> ) } }
jsx//Header.jsx export default class Header extends Component { render() { return ( <div className="page-header"><h2>React Router Demo</h2></div> ) } }
jsx//About.jsx export default class About extends Component { render() { return ( <h3>我是About的内容</h3> ) } }
jsx//Home.jsx export default class Home extends Component { render() { return ( <h3>我是Home的内容</h3> ) } }
-
引入并使用路由API注册路由
导航区的a标签改为Link标签
jsx<Link to="/xxxxx">Demo</Link>
展示区写Route标签进行路径的匹配
jsx<Route path='/xxxx'component={Demo}/>
jsximport { Link, BrowserRouter, Route } from 'react-router-dom' export default class App extends Component { render() { return ( <div> <div className="row"> <div className="col-xs-offset-2 col-xs-8"> <Header/> </div> </div> <div className="row"> <div className="col-xs-2 col-xs-offset-2"> <div className="list-group"> {/* 原生html中,靠<a>跳转不同的页面 */} {/* <a className="list-group-item active" href="./about.html">About</a> <a className="list-group-item" href="./home.html">Home</a> */} {/* 在React中靠路由链接实现切换组件--编写路由链接 */} {/* <BrowserRouter> */} <Link className="list-group-item" to="/about">About</Link> <Link className="list-group-item" to="/home">Home</Link> {/* </BrowserRouter> */} </div> </div> <div className="col-xs-6"> <div className="panel"> <div className="panel-body"> {/* 注册路由 */} {/* <BrowserRouter> */} <Route path="/about" component={About} /> <Route path="/home" component={Home} /> {/* </BrowserRouter> */} </div> </div> </div> </div> </div> ) } }
注意点:
是两种路由模式中的一种,需要将路由链接和注册路由的标签写在同一个标签中产生关联,为了简化后期编码的便利,一般都是直接用标签将父组件包裹起来,这样整个应用中子组件都可以产生路由关联
jsimport {BrowserRouter} from 'react-router-dom' createRoot(document.getElementById('root')).render(<BrowserRouter><App/></BrowserRouter>)
b.路由组件与一般组件
将案例说,
Header
组件为一般组件,About
和Home
组件为路由组件,所以可以将About
和Home
组件放在一个名为pages
文件夹中,这个文件夹专门用来存放路由组件文件,components
文件夹用来存放一般组件的文件
路由组件与一般组件的区别:
1.写法不同:
一般组件:
jsx
<Demo/>
路由组件:
jsx
<Route path:="/demo"component={Demo}/>
2.存放位置不同:
一般组件:components
路由组件:pages
3.接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性:
history:
go:f go(n)
goBack:f goBack()
goForward:f goForward()
push:f push(path,state)
replace:f replace(path,state)
location:
pathname:"/about"
search:"
state:undefined
match:
params:{}
path:"/about"
url:"/about"
jsx
//About.jsx
export default class About extends Component {
render() {
console.log('About路由组件:',this.props);
return (
<h3>我是About的内容</h3>
)
}
}
c.NavLink与封装NavLink
需求:当点击某个组件链接时,该链接会高亮。
实现:
- 原生JS:通过链接元素的点击事件将高亮样式
active
加到该链接元素上- CSS:通过元素的
:hover
伪类选择器为元素添加高亮效果- Vue:通过
@click
事件通过判断条件将高亮样式active
加到该链接元素上- React:通过
NavLink
标签实现,且该标签的默认高亮样式名就为active
html
<!-- index.html -->
<head>
<meta charset="UTF-8">
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./css/bootstrap.css">
<style>
.atguigu{
background-color: rgb(209, 137, 4) !important;
color: white !important;
}
</style>
<title>08</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
jsx
//App.jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* NavLink可以实现路由链接的高亮通过activeClassName指定样式名 */}
<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="atguigu" className="list-group-item" to="/home">Home</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
</div>
</div>
</div>
</div>
)
}
}
封装NavLink
可以通过封装
NavLink
的方式来减少几个NavLink
标签实际应用时重复相同的内容
jsx
//MyNacLink.jsx
import { NavLink } from 'react-router-dom'
export default class MyNacLink extends Component {
render() {
const {to,title}=this.prosp
return (
<NavLink activeClassName="atguigu" className="list-group-item" to={to}>{title}</NavLink>
)
}
}
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/about" title="About"/>
<MyNacLink to="/home" title="Home"/>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
</div>
</div>
</div>
</div>
)
}
}
不足之处在于假如在
MyNacLink
组件中传递了太多的数值,那么MyNacLink
组件取值将会变得繁琐,比如
jsx
<MyNacLink to="/about" title="About" a="1" b="2"/>
- 通过解构赋值的方式将数值从
this.props
中取很繁琐:title
值需要另外通过{}
取
jsx
export default class MyNacLink extends Component {
render() {
const {to,title,a,b}=this.props
return (
<NavLink activeClassName="atguigu" className="list-group-item" to={to} a={a} b={b}>{title}</NavLink>
)
}
}
假如将
title
值写在标签体中,这样就可以通过this.props.children
可以获取标签体内容:
jsx
//标签体内容是一个特殊的标签属性
<MyNacLink to="/about" a="1" b="2">About</MyNacLink>
jsx
export default class MyNacLink extends Component {
render() {
console.log(this.props);
// const {to,title}=this.props
return (
<NavLink activeClassName="atguigu" className="list-group-item" to={to}>{title}</NavLink>
)
}
}
这样就可以通过
...
运算符将直接将this.props
中所有的值都传递到NavLink
组件中:
jsx
export default class MyNacLink extends Component {
render() {
return (
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}>{title}</NavLink>
)
}
}
d.Switch的使用
通常情况下,
path
和component
是一一对应的关系,但是不免有时候会重复path
,这样会造成同时显示两个组件的内容:
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/about" a="1" b="2">About</MyNacLink>
<MyNacLink to="/home">Home</MyNacLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/home" component={Text} />
</div>
</div>
</div>
</div>
</div>
)
}
}
可以使用
Switch
来解决这个问题,Switch可以提高路由匹配效率(单一匹配)。
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/about" a="1" b="2">About</MyNacLink>
<MyNacLink to="/home">Home</MyNacLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/home" component={Text} />
</Switch>
</div>
</div>
</div>
</div>
</div>
)
}
}
e.解决多级路径刷新页面样式丢失的问题
假如在路由匹配路径为多重,那么当网页刷新时就会出现样式丢失的问题:
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/atguigu/about" a="1" b="2">About</MyNacLink>
<MyNacLink to="/atguigu/home">Home</MyNacLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route path="/atguigu/about" component={About} />
<Route path="/atguigu/home" component={Home} />
</Switch>
</div>
</div>
</div>
</div>
</div>
)
}
}
问题:为什么会造成样式的丢失呢?
回答:在刷新后,请求的
bootstrap.css
的响应内容发现了变化:
刷新前请求的bootstrap.css
的响应内容为一段CSS样式代码:
刷新后请求的
bootstrap.css
的响应内容为一段HTML代码,而且返回的HTML网页内容就是当前react
脚手架项目的index.html
文件的内容:
**分析原因:**刷新前后请求bootstrap.css
的路径不一样
- 刷新前的路径:
-
刷新后的路径:
可以看到脚手架在请求
bootstrap.css
资源时,错误的将/atguigu
也当成了请求路径的一部分,因此导致请求不到正确的样式资源。在脚手架中有一个配置,当脚手架请求不到正确内容时,会将根目录
public
下的index.html
文件返回作为响应
解决方案:
-
将引入样式文件的相对路径进行修改
html<link rel="stylesheet" href="/css/bootstrap.css">
-
在引入样式文件的路径中添加
%PUBLIC_URL%
html<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
-
将路由模式修改为
HashRouter
jsimport { HashRouter } from "react-router-dom"; createRoot(document.getElementById('root')).render(<HashRouter><App/></HashRouter>)
f.路由的严格匹配与模糊匹配
在路由匹配中有严格匹配与模糊匹配两种模式,默认是模糊模式,因此可以在路由匹配正确路径后面添加其他内容:
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/about" a="1" b="2">About</MyNacLink>
<MyNacLink to="/home/a/b">Home</MyNacLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route exact path="/about" component={About} />
<Route exact path="/home" component={Home} />
</Switch>
</div>
</div>
</div>
</div>
</div>
)
}
}
需要注意:模糊匹配只会匹配路径的第一个路径字段,如果第一个匹配不上也不会往后匹配了
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/about" a="1" b="2">About</MyNacLink>
<MyNacLink to="/a/home/b">Home</MyNacLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</Switch>
</div>
</div>
</div>
</div>
</div>
)
}
}
开启严格模式:
jsx
<Route exact={true}path="/about"component={About}
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/about" a="1" b="2">About</MyNacLink>
<MyNacLink to="/home/a/b">Home</MyNacLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route exact path="/about" component={About} />
<Route exact path="/home" component={Home} />
</Switch>
</div>
</div>
</div>
</div>
</div>
)
}
}
- 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配:
<Route exact={true}path="/about"component={About}/>
- 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
g.Redirect重定向
当需要组件一加载就显示某个路由组件时,可以使用
Redirect
重定向到指定的路由组件
jsx
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNacLink to="/about">About</MyNacLink>
<MyNacLink to="/home">Home</MyNacLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Redirect to="/home"/>
</Switch>
</div>
</div>
</div>
</div>
</div>
)
}
}
3.路由传递参数
1).嵌套路由
需求:
实现:
- 注册子路由时要写上父路由的path值
- 路由的匹配是按照注册路由的顺序进行的
jsx
//Home.js
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>
<Switch>
<Route path="/home/news" component={News} />
<Route path="/home/message" component={Message} />
<Redirect to="/home/news"/>
</Switch>
</div>
</div>
)
}
}
2).向路由组件传递参数
需求:
a.向路由传递params参数
-
路由链接(携带参数):
jsx<Link to='/demo/test/tom/18'}>详情</Link>
-
注册路由(声明接收):
jsx<Route path="/demo/test/:name/:age"component={Test}/>
jsxexport default class Message extends Component { state = { messageArr: [ { id: '001', title: '消息1' }, { id: '002', title: '消息2' }, { id: '003', title: '消息3' }, ] } 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> </li> ) }) } </ul> <hr /> <Route path="/home/message/detail/:id/:title" component={Detail}/> </div> ) } }
-
接收参数:
jsxconst{id,title}=this.props.match.params
jsxconst DetailData = [ { id: '001', content: "你好,中国" }, { id: '002', content: "你好,比奇堡" }, { id: "003", content: "你好,海绵宝宝" } ] export default class Detail extends Component { render() { // 接收params参数 console.log(this.props); 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> ) } }
b.向路由传递search参数
-
路由链接(携带参数):
jsx<Link to='/demo/test?name:=tom&age=18'}>详情</Link>
-
注册路由(无需声明,正常注册即可):
jsx<Route path="/demo/test"component={Test}/>
jsxexport default class Message extends Component { state = { messageArr: [ { id: '001', title: '消息1' }, { id: '002', title: '消息2' }, { id: '003', title: '消息3' }, ] } render() { const { messageArr } = this.state return ( <div> <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> <hr /> {/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */} {/* search参数无需声明接收,正常注册路由即可 */} <Route path="/home/message/detail" component={Detail}/> </div> ) } }
-
接收参数:
jsxconst{search}=this.props.location
-
备注:获取到的
search
是urlencoded
编码字符串,需要借助querystring
解析jsximport qs from "query-string" console.log(qs.stringify({name:"tom",age:18}));
urlencoded
编码方式:
let obj={name:"tom",age:18} => name=tom&age=18 key=value&key=value
jsxconst DetailData = [ { id: '001', content: "你好,中国" }, { id: '002', content: "你好,比奇堡" }, { id: "003", content: "你好,海绵宝宝" } ] export default class Detail extends Component { render() { // 接收search参数 console.log(this.props); const {search}=this.props.location // search.slice(1):去掉字符串前的? 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> ) } }
c.向路由传递state参数
-
路由链接(携带参数):
jsx<Link to={{path:'/demo/test',state:{name:'tom',age:18})}>详情</Link>
-
注册路由(无需声明,正常注册即可):
jsx<Route path:="/demo/test" component={Test}/>
jsxexport default class Message extends Component { state = { messageArr: [ { id: '001', title: '消息1' }, { id: '002', title: '消息2' }, { id: '003', title: '消息3' }, ] } render() { const { messageArr } = this.state return ( <div> <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> <hr /> {/* state参数无需声明接收,正常注册路由即可 */} <Route path="/home/message/detail" component={Detail}/> </div> ) } }
-
接收参数:
this,props.location.state
jsxconst DetailData = [ { id: '001', content: "你好,中国" }, { id: '002', content: "你好,比奇堡" }, { id: "003", content: "你好,海绵宝宝" } ] 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> ) } }
-
备注:刷新也可以保留住参数
3).push模式和replace模式
- push模式相当于history模式,是在浏览器上一条浏览记录添加覆盖一条新的浏览记录
- replace模式相当于hash模式,是将浏览器上一条替换成新的浏览记录
在注册路由时开启replace模式:
jsx
<Route replace={true} path="/home/message/detail" component={Detail}/>
4.编程式路由导航
当需要在事件中进行跳转路由时,就可以使用编程式路由。
我们知道在路由组件的
props
中包含有history
、location
、match
,其中history
中的go
、goBack
、goForward
、push
、replace
三个方法就可以供我们实现编程式路由导航
a.基本使用
jsx
export default class Message extends Component {
state = {
messageArr: [
{ id: '001', title: '消息1' },
{ id: '002', title: '消息2' },
{ id: '003', title: '消息3' },
]
}
replaceShow = (id, title) => {
this.props.history.replace(`/home/message/detail`)
}
pushShow = () => {
this.props.history.push(`/home/message/detail`)
}
back=()=>{
this.props.history.goBack()
}
forward=()=>{
this.props.history.goForward()
}
render() {
console.log(this.props);
const { messageArr } = this.state
return (
<div>
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
<Link to={{ pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } }}>{msgObj.title}</Link>
<button onClick={() => this.pushShow()}>push查看</button>
<button onClick={() => this.replaceShow()}>replace查看</button>
</li>
)
})
}
</ul>
<hr />
<Route path="/home/message/detail" component={Detail} />
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
</div>
)
}
}
b.路由跳转+传递参数
①.传递param参数
jsx
export default class Message extends Component {
state = {
messageArr: [
{ id: '001', title: '消息1' },
{ id: '002', title: '消息2' },
{ id: '003', title: '消息3' },
]
}
replaceShow = (id, title) => {
// replace跳转+传递param参数
this.props.history.replace(`/home/message/detail/${id}/${title}`)
}
pushShow = (id, title) => {
// push跳转+传递param参数
this.props.history.push(`/home/message/detail/${id}/${title}`)
}
back=()=>{
this.props.history.goBack()
}
forward=()=>{
this.props.history.goForward()
}
render() {
console.log(this.props);
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>
<button onClick={()=>this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
<button onClick={()=>this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
</li>
)
})
}
</ul>
<hr />
<Route path="/home/message/detail/:id/:title" component={Detail}/>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
</div>
)
}
}
②.传递query参数
jsx
export default class Message extends Component {
state = {
messageArr: [
{ id: '001', title: '消息1' },
{ id: '002', title: '消息2' },
{ id: '003', title: '消息3' },
]
}
replaceShow = (id, title) => {
// replace跳转+传递query参数
this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
}
pushShow = (id, title) => {
// push跳转+传递query参数
this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
}
back=()=>{
this.props.history.goBack()
}
forward=()=>{
this.props.history.goForward()
}
render() {
console.log(this.props);
const { messageArr } = this.state
return (
<div>
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
{/* 向路由组件传递search参数 */}
<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
<button onClick={()=>this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
<button onClick={()=>this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
</li>
)
})
}
</ul>
<hr />
<Route path="/home/message/detail" component={Detail}/>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
</div>
)
}
}
③.传递state参数
jsx
export default class Message extends Component {
state = {
messageArr: [
{ id: '001', title: '消息1' },
{ id: '002', title: '消息2' },
{ id: '003', title: '消息3' },
]
}
replaceShow = (id, title) => {
// replace跳转+传递state参数
this.props.history.replace(`/home/message/detail`, { id, title })
}
pushShow = (id, title) => {
// push跳转+传递state参数
this.props.history.push(`/home/message/detail`, { id, title })
}
back=()=>{
this.props.history.goBack()
}
forward=()=>{
this.props.history.goForward()
}
render() {
console.log(this.props);
const { messageArr } = this.state
return (
<div>
<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>
<button onClick={() => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
<button onClick={() => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button>
</li>
)
})
}
</ul>
<hr />
<Route path="/home/message/detail" component={Detail} />
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
</div>
)
}
}
c.withRouter
如果需要使一般路由也有跟路由组件一样的
props
属性以此来实现一些效果,比如浏览记录的回退和前进,可以使用withRouter
让一般组件具备路由组件所特有的API
jsx
//Header.jsx
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()
}
render() {
return (
<div className="page-header">
<h2>React Router Demo</h2>
<button onClick={this.back}>后退</button>
<button onClick={this.forward}>前进</button>
</div>
)
}
}
export default withRouter(Header)
- withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
- withRouter的返回值是一个新组件
5.BrowserRouter与HashRouter的区别
- 底层原理不一样:
- BrowserRouter使用的是H5的nistory API,不兼容IE9及以下版本。
- HashRouter使用的是URL的哈希值。
- url表现形式不一样
- BrowserRouter的路径中没有#,例如:
loca1host:3009/demo/test
- HashRouter的路径包含#,例如:
localhost:3o00/#/demo/test
- BrowserRouter的路径中没有#,例如:
- 刷新后对路由state参数的影响
- BrowserRouter没有任何影响,因为state保存在history对象中。
- HashRouter刷新后会导致路由state参数的丢失。
- 备注:HashRouter可以用于解决一些路径错误相关的问题。