一. 安装
yarn add react-router-dom
二. 常用组件
⏹Router组件:包裹整个应用,一个React应用只需要使用一次
- 两种常用的Router: HashRouter和BrowserRouter
- HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
- 推荐👍 BrowserRouter:使用H5的history API实现(localhost3000/first)
⏹Link组件:用于指定导航链接(a标签)
- 最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
⏹Route组件:指定路由展示组件相关信息
- path属性:路由规则,这里需要跟Link组件里面to属性的值一致
- component属性:展示的组件
- Route写在哪,渲染出来的组件就在哪
三. 主路由使用
import React from "react";
// 导入路由组件
import { BrowserRouter, Redirect, Route } from 'react-router-dom'
function App() {
return (
// 要想使用路由,需要使用Router包裹根组件
<BrowserRouter>
<div className="App">
{/*
路由重定向,当我们访问默认地址的时候,重定向到/home路由,显示Index组件
render: 是一个函数prop,用于指定要渲染的内容
Redirect组件用于实现路由重定向,to属性指定要跳转到的路由地址
⏹有Tabbar的组件需要放到Home组件中,在Home组件中使用子路由
⏹React默认是模糊匹配,如果我们不给默认路由添加 exact 精确匹配的话,当访问 /map 的时候, / 路径也会被匹配到
*/}
<Route exact path="/" render={() => <Redirect to="/home" />}></Route>
<Route path="/home" component={Home}></Route>
{/* ⏹没有Tabbar的组件和Home组件平级 */}
<Route path="/citylist" component={CityList}></Route>
{/*
❗❗只有被路由直接渲染的组件才能通过props获取到路由信息,例如Map组价
Map中使用的其他非直接被路由渲染的组件无法直接从props中获取到路由信息
必须要使用withRouter高阶组件才可以
*/}
<Route path="/map" component={Map}></Route>
{/*
房源详情的路由规则
:id代表路由参数,代表不固定的参数
HouseDetail组件中使用此路由参数(也就是房屋的id)来获取房屋的详情数据
*/}
<Route path="/detail/:id" component={HouseDetail}></Route>
{/* 登录组件的路由地址 */}
<Route path="/login" component={Login}></Route>
{/* 注册组件的路由地址 */}
<Route path="/registe" component={Registe}></Route>
</div>
</BrowserRouter>
);
}
export default App;
四.子路由
import React, { lazy } from 'react'
// 导入路由
import { Route } from 'react-router-dom'
// 导入 TabBar
import { TabBar } from 'antd-mobile'
// 导入组件自己的样式文件
import './index.css'
import Index from '../Index'
const News = lazy(() => import('../News'))
const HouseList = lazy(() => import('../HouseList'))
const Profile = lazy(() => import('../Profile'))
// TabBar 数据
const tabItems = [
{
title: '首页',
icon: 'icon-ind',
path: '/home'
},
{
title: '找房',
icon: 'icon-findHouse',
path: '/home/list'
},
{
title: '资讯',
icon: 'icon-infom',
path: '/home/news'
},
{
title: '我的',
icon: 'icon-my',
path: '/home/profile'
}
]
export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
// 从地址栏中获取,页面初始化时默认选中的Tab菜单
selectedTab: this.props.location.pathname,
// 是否隐藏TabBar
hidden: false,
// 是否全屏
fullScreen: false,
};
}
/*
在此钩子函数中,当路由地址发生变化(Home组件中的子路由被触发)的时候,就会执行
路由信息是通过props传递给组件的,因此通过比较更新前后的两个props中的路由信息
通过此钩子函数实现了路由被切换,组件没有被重新加载时也能实现菜单高亮的效果
*/
componentDidUpdate(preprops) {
/*
preprops中存储着前一次的路由信息
this.props中存储着本次的路由信息
当更新前后的两个路由地址不同的时候,说明发生了路由切换,此时更改当前选中的Tab菜单
*/
if (preprops.location.pathname !== this.props.location.pathname) {
this.setState(() => {
return {
selectedTab: this.props.location.pathname,
}
});
}
}
// 渲染 TabBar.Item
renderTabBarItem() {
// 遍历tabItems数据,动态的创建选项卡
return tabItems.map(item => (
<TabBar.Item
title={item.title}
key={item.title}
// 动态的创建图标
icon={<i className={`iconfont ${item.icon}`} />}
// 选中时的图标
selectedIcon={<i className={`iconfont ${item.icon}`} />}
// 当前路由地址和当前Tab相同的话,设置为选中状态
selected={this.state.selectedTab === item.path}
onPress={() => {
// 当按压下按钮之后,将选中的Tab设置为当前
this.setState({
selectedTab: item.path
})
// 实现路由切换,跳转到相应的组件
this.props.history.push(item.path)
}}
/>
))
}
render() {
return (
<div className="home">
{/* 渲染子路由 */}
<Route path="/home/news" component={News} />
{/*
exact: 精确匹配
父级路由和子级路由是同一个路由地址,只有当路由地址为/home的时候,才会匹配到Index组件
如果我们不添加exact的话,当访问/home/list的时候,由于url中含有/home,Index组件也会被加载出来
*/}
<Route exact path="/home" component={Index} />
<Route path="/home/list" component={HouseList} />
<Route path="/home/profile" component={Profile} />
{/* 动态的创建TabBar */}
<TabBar
tintColor="#21b97a"
barTintColor="white"
// 设置不渲染内容
noRenderContent={true}
>
{/* 调用渲染的方法 */}
{this.renderTabBarItem()}
</TabBar>
</div>
)
}
}
五. withRouter高阶组件
注意:
1.只有被路由直接渲染的组件才能通过props获取到路由信息,例如A组件被路由直接渲染,A组件的子组件B组件因为没有被路由直接渲染,无法通过props获取到路由信息
2.没有被路由直接渲染的组件必须被withRouter
高阶组件包裹之后才能在组件内部通过props获取到路由信息
// 导入withRouter高阶组件
import { withRouter } from 'react-router-dom'
// 从组件的props中解构出history对象
function NavHeader({ history }) {
// 利用react中的路由,返回上一个页面
const defaultHandler = () => history.go(-1);
return (
<div>
<a onClick={defaultHandler}></a>
</div>
)
}
/*
使用withRouter高阶组件包裹NavHeader组件,使其拥有获得路由信息的功能
withRouter(NavHeader)函数的返回值也是一个组件
*/
export default withRouter(NavHeader)
六. props.location.state
handleFavorite = () => {
// 获取出路由相关的对象
const { history, location } = this.props;
const isLogin = '判断用户是否登录';
if (!isLogin) {
// 未登录
return alert('提示', '登录后才能收藏房源,是否去登录?', [
{ text: '取消' },
{
text: '去登录',
// 点击登录按钮之后,跳转到登录页面
onPress: () => {
console.log(location);
/*
{
"pathname": "/detail/5e867853-1faf-7c51",
"search": "",
"hash": "",
"key": "jh8uj6"
}
*/
// 我们在跳转到/login页面之前,将当前页面的location信息添加到路由中
// 这样就可以在Login组件中通过props.location.state获取到我们额外添加的信息
// 在Login组件中根据props.location.state是否存在来判断登录之后跳转到前一个页面还是我们指定的页面
return history.push('/login', {from: location})
}
}
])
}
}
七. props.match.params
我们在路由中进行了如下配置
// 当路由地址为 /detail/XXX 的时候,就会匹配到HouseDetail组件
<Route path="/detail/:id" component={HouseDetail}></Route>
// 这个时候在HouseDetail组件中就可以通过props.match.params获取到路由中指定的id
class HouseDetail extends Component {
state = {}
test = () => {
const { match: { params }} = this.props;
console.log(params.id);
}
}