React-router官方文档
https://reacttraining.com/react-router/web/guides/quick-start
React路由
1.简介
中文文档给出的官方说明是:React Router 是一个基于 React 之上的强大路由库,它可
以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。
2.路由基本配置
(1)首先安装第三方插件
yarn add react-router-dom -D
注:此处必须用yarn安装,否则路由重定向会出现问题。
(2)在整个项目的入口文件,引入该第三方包,并用Router包含App组件。
React router 形式有两种:
HashRouter : 老牌浏览器使用 location.hash + hashchange事件 形式: #/home
BrowserRouter : H5的history来完成 popState事件来完成的 形式: /home
BrowserRouter as Router: as的作用就是给起一个别名
import { BrowserRouter as Router } from ‘react-router-dom’
ReactDOM.render(
<Router>
<App />
</Router>
, document.getElementById('root'));
需要注意的是,如果使用redux进行状态管理,那么需要如下书写:
//引入store
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store = { store }>//注意如有使用路由。路由必须包在Provider里面
<Router>
<App />
</Router>
</Provider>
, document.getElementById('root'));
(3)在需要使用的路由的布局页面,先在react-router-dom中解构出需要的路由配置组
件,并进行配置路由:
//解构出需要的路由配置组件
import { Route,Redirect,Switch} from 'react-router-dom'
● Switch组件
目的是实现一次只渲染一个组件,如果只有一个路由可以不写,但是如果有多个路由必
须使用该组件,确保每次只渲染一个组件。它可以实现类似按需加载组件的作用,起到
一定的性能优化作用
● Redirect 重定向组件
该组件的目的是为了让我们决定在浏览器中打开项目时,首屏的显示内容,比如对于一
般的电商类web app,首屏显示的都是home页面,此时就可以利用该组件,保证每次打
开该web app时,显示的都是home页面。写法如下:
<Redirect from = "/" to = "/home" exact/>
<Route path = "/home" component = { Home }/>
上面Redirect组件中的参数,具体含义为:
from:当前路径
to:目标路径
exact:路由绝对匹配
● Route这里是路由配置组件
该组件的目的就是实现路由路径与路由所要跳转的页面或者说是组件对应。
path:路由路径
component:路由路径对应的页面或者组件
<Route path = "/home" component = { Home }/>
在layout中的index.js(此处的index.js是我设置的总体布局页面)文件中书写完整代码如
下:
//此处引入的是路由对应的组件
import Home from '../../pages/home';
import Category from '../../pages/category/index';
import ShopCar from '../../pages/shop_car/index';
import Mine from '../../pages/mine/index';
import Error from '../../pages/error'
//解构出需要的路由配置组件
import { Route,Redirect,Switch} from 'react-router-dom'
<LayOutContainer>
<Switch>
<Redirect from = "/" to = "/home" exact/>
<Route path = "/home" component = { Home }/>
<Route path = "/category" component = { Category } />
<Route path = "/shopcar" component = { ShopCar } />
<Route path = "/mine" component = { Mine } />
{/* path属性不写就是错误路由匹配 */}
<Route component = { Error } />
</Switch>
</LayOutContainer>
(4)上面的路由配置已经配置好,需要实现点击跳转到对应的页面,所以必须要有a标
签,但是需要对该a标签进行修改,react-router提供了Link和NavLink,这是允许用户浏
览应用的主要方式。 以适当的 href 去渲染一个可访问的锚标签。
Link和NavLink的区别在于,Link只是能实现单纯的路由跳转,但是,NavLink是特殊版
本,它不仅可以实现路由跳转,而且将在与当前URL匹配时为渲染元素添加样式属
性,即在被激活时为元素提供类类名。默认的给定类是active,只需要把需要的激活样式
写在.active类名中即可。这将与activeClassName道具相结合,如下。
import { NavLink , Link } from 'react-router-dom'
<NavLink to="/home" activeClassName="active">
FAQs
</NavLink>
<Link to="/home" >
FAQs
</Link>
(5)React 二级路由实现
在你的以及路由对应的组件中写入导航和路由展示区域就行了,Switch的作用是可以延
续到二级路由中的
<MineContainer>
{/* login/register导航 */}
<NavLink to = "/mine/login"> 登录 </NavLink>
<NavLink to = "/mine/register"> 注册 </NavLink>
{/* login/register 展示区域 */}
<Route path = "/mine/login" component = { Login } />
<Route path = "/mine/register" component = { Register } />
</MineContainer>
(6)编程式导航/常规跳转
可以发现,只有使用了Route的组件身上才会有路由属性:history、location、match,而
具有以上属性的组件我们称之为路由组件,编程式导航和常规跳转都是通过路由组件中
history属性里的方法来实现的。
● 编程式导航–实现固定页面的跳转,比如点击某个按钮跳转到首页
push/replace–用法一样
import React , { Component } from 'react'
class Category extends Component{
goHome=()=>{
const { push , replace } = this.props.history//category组件下的props中有history、location
//match对象,而push和replace就在history中
// push('/home')
replace('/home')
}
render(){
return(
<CategoryContent>
<div onClick={this.goHome}>
<i className="fa fa-home"></i>
</div>
</CategoryContent>
)
}
}
export default Category
● 常规跳转–实现跳转到上一级(这是利用了浏览器的特性)
go/goBack–用法相同
import React , { Component } from 'react'
class Category extends Component{
goHome=()=>{
const { go,goBack } = this.props.history
// go(-1)
goBack(-1)
}
render(){
return(
<CategoryContent>
<div onClick={this.goHome}>
<i className="fa fa-home"></i>
</div>
</CategoryContent>
)
}
}
export default Category
(7). 动态路由
写法一:
<Route path= "/list" component = { List }/>
写法二:
<Route path= "/list/:id" component = { List }/>
以上两个形式都可以,但是按照第二种写法,如果写了/:id之后,路径必须全跟,建议第
一种写法,简单方便。
第二种写法举例:
http://localhost:3000/list/001?a=1&b=2
(8)路由传参/路由接参
● 路由传参:
<Link
to={{
pathname: "/courses",//跳转页面的路由,此处跳转到courses组件页面
search: "?sort=name",//携带的参数以sort=name的形式写在?后面,两个相邻的参数用&连接
}}
/>
● 路由接参:
在对应的courses组件页面,路由传递过来的参数会显示在浏览器的地址栏上面,同时也
在该页面组件props中的location属性的search对象中,所以对该参数进行处理就可以了
const qs = require( 'querystring' )//使用node中的querystring模块对字符串进行处理,当然此处也可以用js中的字符串的方法
const { search } = this.props.location //获取传递过来的参数
const searchObj = qs.parse( search)//对参数进行处理
(9)全局路由监听
路由监听的目的是,只要路由发生变化,能够对对应的页面进行一些操作,比如改变一
些样式,或者增加、删除一些内容等,全局路由监听,需要我们在最大的布局组件中监
听整个项目路由变化情况,但是往往最大的布局组件不是路由组件,它身上没有路由属
性:history、location、match,而且只要这些属性发生变化,都会触发
componentWillReceiveProps钩子函数中,但是可惜的是这些属性只有使用了Route的组
件身上才会有。所以我们必须将该布局组件变成路由组件, 使用 react-router-dom 提供
的高阶组件withRouter , 将布局组件变成伪路由组件。
import React, { Component } from 'react'
import Head from '../common/Head';
import Home from '../../pages/home';
import TabBar from '../common/TabBar';
import styled from 'styled-components'
import { Route,Redirect,Switch,withRouter } from 'react-router-dom'
import Pinkage from '../../pages/pinkage/index';
import Category from '../../pages/category/index';
import ShopCar from '../../pages/shop_car/index';
import Mine from '../../pages/mine/index';
import Error from '../../pages/error'
import List from '../../pages/list/index';
import Detail from '../../pages/detail/index';
class LayOut extends Component {
constructor(props) {
super(props)
this.state = {
backFlag: false,
tabBarFlag: true,
headerTitles: {
'/home': '主页',
'/category': '分类',
'/pinkage': '9.9包邮',
'/mine': '我的',
'/shopcar': '购物车'
},
title: '主页'
}
}
componentDidMount () {
this.checkTabBar()
this.checkBackFlag()
this.checkTitle()
}
componentWillReceiveProps ( nextProps ) {
// 全局路由监听
/*
1. 当我们切换到 9.9 包邮式,底部栏消失
* 思考: 我如何得知我切换到了9.9这个组件上
* url上做判断 , location.pathname
* 在这个钩子函数中,通过this.props得到的属性都是旧的
*/
this.checkTabBar( nextProps )
this.checkBackFlag( nextProps )
this.checkTitle( nextProps )
}
checkTitle = ( nextProps ) => {//该方法是为了实现相应的路由改变头部标题
const { pathname } = nextProps && nextProps.location || this.props.location
this.setState({
title: this.state.headerTitles[ pathname ]
})
}
checkTabBar = ( nextProps ) => {//改变底部栏的消失和出现
/*
componentDidMount 中 pathname 通过 this.props 获取
componentWillReceiveProps 中 通过 参数 nextProps 获取
*/
const { pathname } = nextProps ? nextProps.location : this.props.location
switch ( pathname ) {
case '/pinkage':
this.setState({
tabBarFlag: false
})
break;
default:
this.setState({
tabBarFlag: true
})
break;
}
}
checkBackFlag = ( nextProps ) => {//控制头部返回按钮的出现和隐藏
const { pathname } = nextProps ? nextProps.location : this.props.location
switch ( pathname ) {
case '/pinkage':
this.setState({
backFlag: true
})
break;
default:
this.setState({
backFlag: false
})
break;
}
}
render () {
const { backFlag,tabBarFlag,title } = this.state //结构数据
return (
<LayOutContainer>
<Head title = { title } backFlag = { backFlag } { ...this.props } />
<Switch>
<Redirect from = "/" to = "/home" exact/>
<Route path = "/home" component = { Home }/>
<Route path = "/pinkage" component = { Pinkage } />
<Route path = "/category" component = { Category } />
<Route path = "/shopcar" component = { ShopCar } />
<Route path = "/list" component = { List }/>
<Route path = "/detail" component = { Detail} />
{/* <Route path = "/list/:id" component = { List }/> */}
<Route path = "/mine" component = { Mine } />
{/* <Route path = "/mine/a" component = { () => <div> 123 </div> } /> */}
{/* path属性不写就是错误路由匹配 */}
<Route component = { Error } />
</Switch>
{ tabBarFlag && <TabBar/>}
</LayOutContainer>
)
}
}
export default withRouter( LayOut )