一. 封装AuthRoute鉴权路由组件
- 创建组件 AuthRoute 并导出
- 在AuthRoute组件中返回Route组件(在Route基础上做了一层包装,用于实现自定义功能)、
- 给Route组件添加render方法,指定该组件要渲染的内容(类似于 component 属性)
- 在render方法中,判断用户是否登录
- 如果登录了,就渲染当前组件(通过参数component获取到要渲染的组件,因为组件都是大写开头,因此需要重命名)
- 如果没有登录,就重定向到登录页面,并指定登录成功后要跳转到的页面路径
- 将 AuthRoute 组件接收到的props原样传递给Route组件(保证与Route 组件使用的方式相同)
- 使用AuthRoute 组件配置的路由规则,验证能否实现页面的登录访问控制
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
// 使用方法: <AuthRoute path="..." component={...} 其他属性名="其他属性值" />
const AuthRoute = ({ component: Component, ...rest }) => {
return (
// rest属性存放 path="..."和其他属性名等属性
<Route {...rest} render={props => {
const isLogin = '判断用户是否登录'
if (isLogin) {
// 已经登录,渲染传入的Component组件,将props传递给组件,组件中才能获取到路由的相关信息
return <Component {...props} />
} else {
// 未登录,渲染重定向组件,通过to属性指定跳转的路由信息
return <Redirect to={{
// 指定重定向的url
pathname: '/login',
state: {
/*
未登录时,要访问的页面url,我们可以在登录页面获取到该url,当登录成功之后,可以直接跳转到该url
之所以指定属性名为from,是因为我们在Login组件中配置了要使用🤪props.location.state.from🤪
*/
from: props.location
}
}} />
}
}} />
)
}
export default AuthRoute
二. 使用自定义鉴权路由组件
import React from "react";
// 导入路由组件
import { BrowserRouter, Redirect, Route } from 'react-router-dom'
// 导入自定义的路由鉴权组件
import AuthRoute from './components/AuthRoute'
// 导入登录组件
import Login from './pages/Login'
// 导入登录后才能访问的组件
import Rent from './pages/Rent'
import RentAdd from './pages/Rent/Add'
function App() {
return (
// 要想使用路由,需要使用Router包裹根组件
<BrowserRouter>
<div className="App">
{/* 登录组件的路由地址 */}
<Route path="/login" component={Login}></Route>
{/* ⏹AuthRoute自定义鉴权路由组件,只有登录后才能访问,未登录访问会跳转到/login页面中 */}
<AuthRoute exact path='/rent' component={Rent} />
<AuthRoute path='/rent/add' component={RentAdd} />
</div>
</BrowserRouter>
);
}
export default App;
三.登录组件中的处理
- 如果是主动点击登录按钮访问的login组件,当登录成功之后,会跳转到前页面
- 如果是重定向到login组件,当登录成功之后,会跳转到重定向之前的页面
import React, { Component } from 'react'
// 导入withFormik
import { withFormik } from 'formik'
class Login extends Component {
render() {
return (
// 省略...
)
}
}
/*
使用withFormik高阶组件包装Login组件,为Login组件提供属性和方法
返回的是被高阶组件包裹之后的Login组件
*/
Login = withFormik({
// 省略...
// 为表单提供提交事件(当表单提交的时候,就会触发该函数)
handleSubmit: async (values, { props }) => {
// 解构出用户输入的账号和密码
const { username, password } = values;
// 调用登录的接口
const res = await axios.post('/user/login', {
username,
password
});
// 解构后端返回的值
const { status, body } = res.data;
// 登录成功
if (status === 200) {
// 将token值保存到浏览器中
localStorage.setItem('项目token名称', body.token);
// 当props.location.state为空时,说明是直接进入的登录页面,登录成功之后返回上一个页面即可
if (!props.location.state) {
/*
返回上一个页面
注意: 无法在该方法汇总通过this.props来获取到路由信息(因为this指向当前回调函数,并不指向该组件)
通过handleSubmit方法的第二个参数中解构出props来使用props
*/
props.history.go(-1);
} else {
/*
⏹如果props.location.state不为空,说明未登录却访问了登录之后才能看的页面,然后被重定向到Login登录页面
这个时候props.location.state中会含有原先页面的url(我们在鉴权组件AuthRoute中进行了配置)
⏹props.history.replace()和props.history.push()的区别
※rent页面是登录之后才能访问的页面
当我们从首页点击右上角地图图标的时候,访问顺序如下 首页 -> Login登录页面 -> rent页面
当我们进入rent页面之后,点击左上角的返回按钮的时候
1. 如果是props.history.push()的情况下,访问路径被记录在这样的数组里 ['home', 'login', 'rent']
所以当我们在Rent页面点击返回上一个页面的时候,会找到第二个元素 login, 跳转到Login登录页面
我们已经登录成功了,所以不需要返回login页面
2. 如果是props.history.replace()的情况下,当我们访问到Login组件的时候,访问的路径被记录在这样的数组里
['home', 'login'],我们登录成功之后,进入rent页面,因为使用的是replace(),所以 /login 会被替换为 /rent
也就说数组变成了['home', 'rent'],所以在rent页面返回前一个页面的时候,跳转到的是 /home 而不是 /login
*/
props.history.replace(props.location.state.from.pathname);
// ⏹因为我们在props.location.state.from中使用了from,因此rent组件中也要配置🤪from🤪
}
}
}
})(Login)
// 注意: 此处返回的是被高阶组件包装后的Login组件
export default Login