react技术栈构建后台管理系统模版

之前一直开发的技术栈主要是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-admin-system

运行效果

开发这个管理系统主要目的是理清React-Router v4React-Redux、Redux等常用类库的布置和使用。在掘金浏览许多关于React技术开发的文章。关于管理系统对于用户登录权限认证和系统路由布置使用以及对ajax第三方类库二次封装等文章比较少。所以下面会比较着重描述一下个人开发过程中这部分的实现。

login

DataCount

progress

目录分析

开发对于请求使用的是第三方请求库axios,对于axios的封装主要在平时Vuejs开发时候对请求封装实践??⒅饕糠质?code>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.pushReact-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)吗?

React-admin-system

参考文章

[译] 2019 React Redux 完全指南

[译]React中的用户认证(登录态管理)

React Router 4.x开发,这些雷区我们都帮你踩过了

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • 完整项目地址:vue-element-admin系类文章一:手摸手,带你用vue撸后台 系列一(基础篇) 前言 拖...
    7cd975786ccd阅读 10,697评论 4 65
  • element-ui 文档 Vue项目接口文档地址 博客 session 和 cookie等 学什么? 1 如何使...
    cj_jax阅读 3,943评论 0 10
  • 第一部分主要使用React提供的组件,创建博客页面的展示,包括文章列表页面和文章详情页面。此次系列文章主要面向Re...
    SamDing阅读 6,372评论 4 20
  • 叶子黄了,麦子也黄了。 翻滚的热浪扶在脸庞 土生土长的庄稼人像极了麦穗 赴一场收获的旅程 看在眼里的季节荧荧曳曳,...
    4afdcb2758e4阅读 149评论 0 1
  • 有些事情不做永远都不知道结果,只有尝试才能收获意外惊喜。 拿我今天尝试做饭来说吧。我们在这里住了四年多,基本没做过...
    水漓月阅读 234评论 0 0