快速打造 react 后台管理系统

项目预览地址

前言

相信不少小伙伴都有可能碰到开发后台管理系统这样的需求,那么咱们该如何快速的完成这个需求呢css

本文将以 react 为切入点,记录打造一个基础管理系统模板的过程,以此加深对 react 技术栈以及项目实战的理解,但愿对你们开发一个这样的项目有所帮助html

若是文章中有出现纰漏、错误之处,还请看到的小伙伴多多指教,先行谢过前端

如下↓react

项目简介

react-admin 是由 create-react-app 脚手架快速构建,基于 React 生态系统搭建的后台管理系统模板。实现了登录/注销、路由懒加载、axios封装、简单权限管理等功能,它能够帮助你快速生成管理系统模板,你只须要添加具体业务代码便可webpack

线上预览地址 预览地址

GitHub 代码 代码地址ios

技术栈

此项目涉及的技术栈主要有 es6reactreact-routerreduxreact-reduxCreate React Appreact-loadableaxios等,因此你可能须要提早了解这些知识,这样会对你了解这个项目有很大的帮助git

基本功能

  • 路由懒加载
  • 面包屑导航
  • 经常使用 UI 展现
  • echarts 全屏展现
  • 登录/注销功能
  • axios 封装
  • 简单权限管理

项目结构

├── public                   # 不参与编译的资源文件
├── src                      # 主程序目录
│   ├── api                     # axios 封装
│   ├── assets                  # 资源文件
│   │   ├── font                    # 字体文件
│   │   └── images                  # 图片资源
│   ├── components              # 全局公共组件
│   │   ├── CustomBreadcrumb        # 面包屑导航
│   │   └── CustomMenu              # menu 菜单
│   ├── contatiners             # 页面结构组件
│   ├── routes                  # 路由目录
│   ├── store                   # redux 配置
│   ├── style                   # 样式目录
│   ├── utils                   # 工具类
│   ├── views                   # UI 页面
│   ├── APP.js                  # App.js
│   └── index.js                # index.js
├── .prettierrc.js           # 代码规范
├── config-overrides.js      # antd 样式按需加载

总体思路

打造一个任何一个项目,除去前期须要考虑的受众群体(其实就是兼容…)以外,再加上技术选型,以后就到了页面架构层。在这个项目中,前期不在咱们的考虑范围以内(已经肯定了有木有),因此就从页面架构开始es6

一个后台管理项目,不管它是否具备权限校验功能,可是有一些公共的部分是应该有的。好比登录页面、公共的头部、侧边栏导航、底部以及错误页面等。这些共有的部分以及权限特有的部分共同组成了这个系统github

划分出这样的模块以后,一个项目基本的页面架构也就完成了,由于这一部分关系到咱们以后对于页面路由的定义,因此我认为是很重要的一部分web

路由

基于 react-router@5.1.1

路由功能能够说是一个 React 项目的关键,经过前面对于页面架构的划分,咱们就能够很流畅的进行路由的注册

基本

react-admin 这个项目中,所使用的是 <HashRouter>

首先,咱们须要区分公共页面和可能的特有页面

<Router>
    <Switch>
        <!--精确匹配是否是在首页-->
        <Route path='/' exact render={() => <Redirect to='/index' />} /> 
        <!-- 错误页面 -->
        <Route path='/500' component={View500} />
        <Route path='/login' component={Login} />
        <Route path='/404' component={View404} />
        <!-- UI页面 -->
        <Route path='/' component={DefaultLayout} />
    </Switch>
</Router>

接下来就能够去注册路由表,再将它循环遍历到咱们的视口页面上。你们可能也发现了循环遍历这个词,操做数组就意味着咱们能够对它进行一系列的筛选,从而实现 路由权限 的控制(这个咱们后面再说)

懒加载

做为一个 SPA 级应用,有不少优点(响应速度更快、良好的先后端分离等等),可是也存在不少缺陷,首次加载耗时过长就是咱们不得不面对的问题

其实从 webpack4.0 开始,它自己已经实现了按需加载组件,可是也有它本身的一些规则(好比文件大小),因此咱们仍是须要对页面的首次加载进行一些处理,而路由就是一个很好的切入点

本项目使用的是 react-loadable,它能够用很简单的方式实现路由的懒加载,设置延迟时间、加载动画、服务端渲染等功能

import Loadable from 'react-loadable';
import Loading from './my-loading-component'; // 这里能够放置你的 loading

const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
});

export default class App extends React.Component {
  render() {
    return <LoadableComponent/>;
  }
}

固然,你也可使用 React.lazySuspense 技术(传送门),不过,它不支持服务端渲染

登陆

登陆的逻辑也很简单:

咱们的项目首页通常状况下是 index 页面,这个时候咱们就须要先去判断一个用户加载进来的时候是否是在登陆状态,若是是那么就正常显示,若是不是就应该跳转到登陆页面

本项目使用的是将用户的登陆信息存储在 localStorage中,注销的时候清除 localStorage

关于 token 能够直接存在本地,后台设定一个过时时间就能够了

还有一种状况就是用户登陆以后,可是因为长时间没有操做致使 token 过时了,这个时候可能就会出现两种选择:

  • 让用户直接跳转到登陆页面从新登陆
  • 查看本地是否存储了用户信息,若是有就更新用户的 token ,让其继续操做,反之则跳转到登陆页面(这个取决于你将用户信息存储在哪里)

固然,具体要怎么作,仍是取决于产品的需求,这里只是提供一种思路

axios 封装

基本操做

项目使用 axios 与后台进行交互,封装的部分有添加请求拦截器、响应拦截器、设置响应时间以及将 token 添加到请求头等功能

import axios from 'axios'

// 这里取决于登陆的时候将 token 存储在哪里
const token = localStorage.getItem('token')

const instance = axios.create({
    timeout: 5000
})

// 设置post请求头
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

// 添加请求拦截器
instance.interceptors.request.use(
    config => {
        // 将 token 添加到请求头
        token && (config.headers.Authorization = token)
        return config
    },
    error => {
        return Promise.reject(error)
    }
)

// 添加响应拦截器
instance.interceptors.response.use(
    response => {
        if (response.status === 200) {
            return Promise.resolve(response)
        } else {
            return Promise.reject(response)
        }
    },
    error => {
        // 相应错误处理
        // 好比: token 过时, 无权限访问, 路径不存在, 服务器问题等
        switch (error.response.status) {
            case 401:
                break
            case 403:
                break
            case 404:
                break
            case 500:
                break
            default:
                console.log('其余错误信息')
        }
        return Promise.reject(error)
    }
)

export default instance

这里并无对 baseUrl 进行设置,主要是考虑到项目中可能存在不止一个 url,好比图片这些资源可能存在七牛云或者阿里云这样的服务器上面,然后台接口又是另一个url 了。因此添加了一个 config 文件,导出各个 url

// 考虑到网站可能有好几个域名,因此单独提出来

export const API = 'http://rap2api.taobao.org/app/mock/234047'

export const URLAPI = ''

调用接口的时候就能够直接这样

import {API} from './api/config'
import axios from './api'

axios.get({
    url: API + '/login'
})

固然,若是你并无这样的需求,你彻底能够取消 config 这个文件,将 baseUrl 一并封装进去

一样的,也并无对经常使用的请求好比 getpost 等进行封装,由于使用这些方式的时候可能会对数据作一些特定的操做,好比序列化等等,因此我的感受意义并非很大

跨域

这里顺便记录一下跨域问题的解决方式:

若是你没有使用 npm run ejectwebpack 的配置暴露出来,能够直接在 package.json 中配置 proxy

"proxy": {
    "/api": {
      "target": "http://100.100.100.100", //后端地址
      "changeOrigin": true
    }
 }

这样只须要在 baseUrl 后面添加 /api 就能够了

固然,若是你将 webpack 中的配置暴露出来了,那么也能够直接在 config 文件中进行设置,都是能够实现一样的效果

权限

权限功能基本上是后台管理项目中不可或缺的部分

通常状况下,权限的控制体如今页面级别以及按钮级别(用户是否能够访问某个页面或者操做某个按钮,好比新增、删除),这个权限在用户注册、分配或者后期超级管理员更改的时候肯定

项目实现

在这个项目中使用简单的权限控制,但愿能够给你们一些思路,具体实现方式:

  • 用户登陆,从后台获取注册时的角色(权限标识)
  • 经过权限标识,对注册的 menu 菜单进行过滤,渲染到页面
getMenu = menu => {
        let newMenu,
            auth = JSON.parse(localStorage.getItem('user')).auth // 获取存储的用户权限标识
        if (!auth) {
            return menu
        } else {
            // 过滤注册的 menu
            newMenu = menu.filter(res => res.auth && res.auth.indexOf(auth) !== -1)
            return newMenu
        }
    }
  • 经过权限标识,对注册的路由数组进行过滤
{routes.map(item => {
    return (
        <Route
            key={item.path}
            path={item.path}
            exact={item.exact}
            render={props =>
                !auth ? (
                    <item.component {...props} />
                ) : item.auth && item.auth.indexOf(auth) !== -1 ? (
                    <item.component {...props} />
                ) : (
                    // 这里也能够跳转到 403 页面
                    <Redirect to='/404' {...props} />
                )
            }></Route>
    )
})}
  • 按钮权限,直接使用权限标识判断能否操做,隐藏或者展现便可

说明:这里对注册的路由数组进行过滤这一步进行说明,通常状况下前端路由都是提早注册好的,就算没有 menu 菜单导航,若是咱们在地址栏直接输入路径也是能够访问的,这里进行一次过滤以后就能够避免这种状况。固然,咱们也能够给每个权限设定一个能够访问的路径数组,经过比较跳转的地址是否存在这个数组当中来进行相应的展现

后台控制

这里也简单说一下后台控制权限的案例,对于前端来讲要简单不少

  • 用户登陆,拿到须要展现的 menu 数组,直接渲染到页面(对菜单的筛选由后台完成)
  • 经过权限标识,判断用户有没有某个按钮的操做权限

至于用户在地址栏直接输入地址去访问,这里有两种状况:

  • 若是用户没有访问某一个页面的权限,那么使用其 token 请求后台数据的时候必定是不成功的,咱们能够将这一个操做封装在 axios 请求中,经过不一样的状态码进行页面跳转
  • 我就是访问了一个没有请求的页面(这个页面还不给没权限的人看),那咱们就采用过滤权限数组的方式对其操做进行阻止

固然,这里的第二种状况不多见…

其它

项目中还集成了平时可能会遇到的一些功能需求

动画

动画使用的是 Animate.css 动画库,使用方式

// 下载
yarn add animate.css

// link标签引入
<link rel="stylesheet" href="animate.min.css">

// 或者 import 引入
import 'animate.css'

而后在须要的盒子上面添加相应的类名便可,能够设置入场、离场动画,也能够设置动画时间、延时等

富文本

富文本编辑器使用的是 Antd 官方推荐的 braft-editor

一个基于 draft-jsWeb富文本编辑器,适用于 React框架,兼容主流现代浏览器

使用方式:

// 引入
import BraftEditor from 'braft-editor'

// 可使用 BraftEditor.createEditorState 方法来将 raw 或者 html 格式的数据转换成 editorState 数据
editorState: BraftEditor.createEditorState('你好,<b>可爱的人! 很幸运在这里与你相遇!</b>')

更多具体的组件属性及实例方法,你们能够参考其文档 传送门

echarts

echarts 相信你们不会陌生,百度的文档也很清晰,这里单独提出来主要记录开发过程当中遇到的一个问题

在页面窗口变化的状况下咱们可使用

window.addEventListener('resize', function() {
    myChart.resize()
})

这种方式保证 echarts 的正常自适应

但是当咱们在点击菜单收缩展开按钮的时候并不会触发 window.resize 方法,其实页面盒子的宽度已经发生了变化,只是 echartsresize 事件已经触发结束了,这个时候咱们只须要在 componentDidUpdate 这个生命周期中注册一个定时器延时触发 resize 事件就解决了,只是别忘了在 componentWillUnmount 生命周期中清除掉这个定时器

加载进度条

加载进度条使用的是 nprogress ,使用方式也很简单

// 下载
yarn add nprogress

// 引入
import NProgress from 'nprogress'

// 开始加载
NProgress.start();

// 加载结束
NProgress.done();

// 移除进度条
NProgress.remove();

全屏插件

全屏功能使用的是 screenfull 插件

使用方式

// 下载
yarn add screenfull

if (screenfull.isEnabled) {
    screenfull.request(); // 全屏
}

.exit() // 退出
.toggle() // 可切换

也能够给它 注册 change 事件

if (screenfull.isEnabled) {
    screenfull.on('change', () => {
        console.log('Am I fullscreen?', screenfull.isFullscreen ? 'Yes' : 'No');
    });
}

可是别忘了移除掉这个事件

screenfull.off('change', callback);

代码格式统一

Create React App 提供了一组最多见的错误规则,在代码运行的时候会有错误信息提示,因此 Eslint 规则在这里并非必须的,若是你也想在代码书写的时候就提示错误能够进行下面这个步骤:

下载 eslint
yarn add eslint
添加一个 .eslintrc.js 文件或者在 package.json 文件中的 eslintConfig 对象中直接添加你须要使用的规则

更多规则能够参考这里

项目中并无使用 eslint,只是添加了 pretter 为项目统一了编码风格

至于如何在项目中集成 pretter ,具体的使用方式能够参考 官方文档,这里就不在叙述了

webpack拓展

相信你们在开发这样的项目的时候,确定或多或少会有一些定制化的东西须要修改 webpck 的配置

当咱们使用 create react app 建立项目的时候,webpack 的配置默认是不暴露出来的,若是咱们想修改它的配置,可使用 yarn eject 或者 npm run eject 命令将其暴露出来再去修改

可是,若是咱们并不想暴露 webpack 的配置,还想去配置它该怎么办呢

antd 的样式按需加载就是经过这种方式实现的

react-app-rewiredreact 社区开源的一个修改 CRA 配置的工具,例如扩展 Create React AppWebpack 配置,而 customize-cra 提供了一组用于自定义利用 react-app-rewired 核心功能的 Create React App 配置, 能够经过 config-overrides.js 文件来对 webpack 配置进行扩展

简单点: 使用 react-app-rewiredcustomize-cra 能够实现咱们的需求

使用方式:

  • 首先确定是先下载这两个包了
  • 而后在咱们的项目根目录建立一个 config-overrides.js 文件

    // 文件里面这么写,好比说咱们设置一个绝对路径

const { override, addWebpackAlias } = require('customize-cra')
const path = require('path')

module.exports = override(

addWebpackAlias({        
    ['@']: path.resolve(__dirname, 'src')   
})

)

- 最后修改 `package.json` 文件中的 `scripts`

"scripts": {

"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"

}

结束,这样咱们就能够在项目中愉快地使用这个 `@` 了

固然,`customize-cra` 这个包为咱们还提供了不少封装好的 `API`,详情请 [参见这里](https://github.com/arackaf/customize-cra/blob/master/api.md)



### 最后

这个项目都是本人闲暇时间开发,主要是为了熟悉 `react` 开发流程以及其周边生态的使用,项目仍是比较简陋,后期会进行迭代开发,将其打形成一个更加实用的后台管理模板

若是以为不错或者对你有些许的帮助,欢迎 `star`,或者你有更好的实现方式、有趣的 `idea`,也欢迎留言交流
相关文章
相关标签/搜索