之前一直开发的技术栈主要是
VueJS
相关,平时也有阅读React
文档,但是没有把React
相关技术栈串联起来,形成一个后台系统的模板。在学习的过程之中,基于React
开发推荐的create-react-app
脚手架搭建管理系统模板。
开发依赖
antd: "^3.19.1",
axios: "^0.19.0",
bizcharts: "^3.5.3",
react: "^16.8.6",
react-dom: "^16.8.6",
react-redux: "^7.0.3",
react-router-dom: "^5.0.0",
react-transition-group: "^4.1.0",
redux: "^4.0.1"
git仓库地址
运行效果
开发这个管理系统主要目的是理清React-Router v4
、React-Redux
、Redux
等常用类库的布置和使用。在掘金浏览许多关于React
技术开发的文章。关于管理系统对于用户登录权限认证和系统路由布置使用以及对ajax
第三方类库二次封装等文章比较少。所以下面会比较着重描述一下个人开发过程中这部分的实现。
DataCount
progress
目录分析
开发对于请求使用的是第三方请求库axios
,对于axios
的封装主要在平时Vuejs
开发时候对请求封装实践。开发主要部分是util/fakeAuth.js
和router/PrivateRoute.js
,这部分是对用户登录状态验证。而components/ContentMain
是登录后的主要路由设置。
├─public(静态资源目录)
├─screenShot(开发现阶段运行截图)
├─service(service服务Koa)
│ ├─bin
│ ├─config
│ ├─controller
│ ├─model
│ ├─routes
│ └─util
└─src
├─api(请求二次封装)
├─assets(开发静态资源目录)
│ └─images
├─components(图标类组件)
│ ├─AreaMap
│ ├─Chart
│ ├─ContentMain
│ ├─Diagram
│ ├─Line
│ ├─Map
│ └─SiderNav(sider侧边导航栏)
├─redux(store组件)
├─router(路由组件)
├─util(权限验证函数)
└─views(主要开发组件)
├─Alert
├─Avatar
├─Basic
├─Cascader
├─Checkbox
├─DataChart
├─Form
├─Index
├─Login
├─Message
├─NoMatch
├─Progress
└─Spin
login用户登录状态验证
包装PrivateRoute权限路由
对React-Router
的路由组件Route
,包装路由组件使需要登录获取权限的路由,需要自动认证再进行渲染。渲染组件需要通过权限认证函数fakeAuth.authenticate()
进行验证,该函数会判别是否存在后端接口返回的token值,分别渲染不同组件。
import React from 'react';
import {Route,Redirect} from 'react-router-dom';
import {fakeAuth} from '../util/fakeAuth';
const PrivateRoute = ({component:Component,...rest})=>{
return (
<Route
{...rest}
render = {props => (fakeAuth.authenticate())?(<Component {...props}/>):(
<Redirect to={{
pathname:"/login",
state:{from:props.location}
}}/>
)}
></Route>
)
}
export default PrivateRoute;
fakeAuth
验证函数
fakeAuth
文件包含三个函数authenticate
、setToken
、signout
,以上分别对应验证登录权限、设置登录token、清除登录token。
export const fakeAuth = {
authenticate() {
const token = sessionStorage.getItem("loginToken");
return !!token ? true : false;
},
setToken(token) {
sessionStorage.setItem("loginToken", token);
return true;
},
signout() {
sessionStorage.removeItem('loginToken');
}
};
login登录验证详细
login.js
通过fakeAuth.authenticate()
验证,然后分别渲染不同组件。<Redirect>
组件在这里主要有两个作用:
- 用户登录获取权限路由权限后,在浏览器地址栏重新输入
/login
路由会自动返回权限路由路由首页。 - 对于未登录用户,自动跳转到登录页。
import React,{Component} from 'react';
import {Redirect} from 'react-router-dom';
import {fakeAuth} from '../../util/fakeAuth';
import LoginForm from './index';
export default class Login extends Component{
render(){
return (
fakeAuth.authenticate()?<Redirect to="/"></Redirect>:<LoginForm />
)
}
}
登录请求代码
登录认证请求代码在views/login/loginForm
中,handleSubmit
是登录事件函数,主要对获取用户权限接口获取token,然后进行路由跳转.this.props.history.push
是React-Router v4
版本特有的写法,其他版本可以参考官方文档。要获取到this.props.history
需要withRouter
对组件传入location,match,history
等信息。
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
//这里需要对后端接口请求,调试中
fakeAuth.setToken("zxcvbnmasdfghjkl");
this.props.history.push('/dataCount');
message.success('登陆成功',1);
return;
}
});
}
App.js
入口文件
login
是公开的路由,其他需要权限的路由包含在PrivateRoute
组件中,最后一个如果没有组件匹配,最后匹配的是我们自定义的404页面
。
import React from 'react';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Provider} from 'react-redux';
import {store} from './redux/index.js';
import {CSSTransition} from "react-transition-group";
import Login from './views/Login/login';
import Index from './views/Index';
import PrivateRoute from './router/PrivateRoute';
import NoMatch from './views/NoMatch';
import history from './api/history';
import './App.css';
function App() {
return (
<Provider store={store}>
<Router basename="/echo" history={history}>
<div className="entryWrap">
<CSSTransition
classNames="fade"
timeout={1000}
>
<Switch>
<Route path="/login" component={Login} exact/>
<PrivateRoute path="/" component={Index}/>
<Route component={NoMatch}/>
</Switch>
</CSSTransition>
</div>
</Router>
</Provider>
);
}
export default App;
ContentMain
权限路由路由配置页面
ContentMain
主要是配置了权限路由的配置。使用PrivateRoute
包装路由,方便性由此可见。配置路由没有比较难逻辑,如下所示:
import React,{Component} from 'react';
import {Switch,withRouter} from 'react-router-dom';
import PrivateRoute from '../../router/PrivateRoute';
import DataChart from '../../views/DataChart';
import Basic from '../../views/Basic';
import Form from '../../views/Form';
import Message from '../../views/Message';
import Alert from '../../views/Alert';
import Spin from '../../views/Spin';
import Progress from '../../views/Progress';
import Checkbox from '../../views/Checkbox';
import Cascader from '../../views/Cascader';
import NoMatch from '../../views/NoMatch';
import './index.css';
class ContentMain extends Component{
render(){
return(
<div className="routeWrap">
<Switch>
<PrivateRoute path="/dataCount" exact component = {DataChart}/>
<PrivateRoute path="/basic" exact component = {Basic}/>
<PrivateRoute path="/form" exact component = {Form}/>
<PrivateRoute path="/alert" exact component = {Alert}/>
<PrivateRoute path="/message" exact component = {Message}/>
<PrivateRoute path="/spin" exact component = {Spin}/>
<PrivateRoute path="/progress" exact component = {Progress}/>
<PrivateRoute path="/checkbox" exact component = {Checkbox}/>
<PrivateRoute path="/cascader" exact component = {Cascader}/>
<Route component={NoMatch}/>
</Switch>
</div>
)
}
}
export default withRouter(ContentMain);
axios
二次封装统一请求处理
在api/index.js
主要包含了对axios
第三方请求库的二次封装,包含错误统一处理以及不同请求方法GET、POST、DELETE、PUT
等统一风格的api。关于对token
请求权限过期,返回请求状态码401
,清除token
,重定向到/logn
处理逻辑也会在这里。在这一部分,Vuejs
的处理逻辑也很类似,但是比较熟悉可以直接通过以下方式进行重定向:
import router from "../router/index.js";
router.replace({
path: "/login",
query: {
redirect: router.currentRoute.path
}
});
但是对于React-Router
,网上浏览比较多的文章,尝试多比较多遍,都没有找到一个不是强制刷新的路由方案,使用window.location.href
可以强制跳转,但是这里使用了createBrowserHistory
方法进行重定向。如果有其他的方案可以给我留言。菜鸡的我才能茁壮成长。下面是关于封装的代码部分:
import axios from "axios";
import {fakeAuth} from '../util/fakeAuth';
import {message as Message} from 'antd';
import {timeout,baseURL} from "./config.js";
import history from './history';
axios.defaults.timeout = timeout;
axios.defaults.baseURL = baseURL;
axios.interceptors.request.use(
config => {
if (fakeAuth.authenticate()) {
config.headers.Authorization = `Bearer ${sessionStorage.getItem('loginToken')}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
fakeAuth.signout();
history.push('/login');
break;
default:
}
const message = error.response.data.message ?error.response.data.message :"服务器异常";
Message.error(message);
}
return Promise.reject(error);
}
);
const gl_ajax = params => {
return axios({
method: params.method.toLowerCase(),
url: `${axios.defaults.baseURL}${params.url}`,
data: params.method !== "get" ? params.data : "",
params: params.method === "get" ? params.data : "",
responseType: params.file ? "blob" : ""
})
.then(res => {
params.success && params.success(res);
})
.catch(err => {
params.error && params.error(err);
});
};
export default gl_ajax;
history
文件部分是参考了网友给的技术方案,利用了createBrowserHistory({forceRefresh:true})
,这里show
出来给大家参考一下:
// history.js
import { createBrowserHistory } from "history";
export default createBrowserHistory({forceRefresh:true});
// config.js
export const timeout = 5000;
export const baseURL =
process.env.NODE_ENV === "production"
? "https://echo/api/v1"
: "http://localhost:8080/api/v1";
如果觉得喜欢可以给个小星星(star)吗?
参考文章
[译] 2019 React Redux 完全指南
[译]React中的用户认证(登录态管理)
React Router 4.x开发,这些雷区我们都帮你踩过了
网友评论