快速打造 react 後臺管理系統

項目預覽地址css

前言

相信不少小夥伴都有可能碰到開發後臺管理系統這樣的需求,那麼咱們該如何快速的完成這個需求呢html

本文將以 react 爲切入點,記錄打造一個基礎管理系統模板的過程,以此加深對 react 技術棧以及項目實戰的理解,但願對你們開發一個這樣的項目有所幫助前端

若是文章中有出現紕漏、錯誤之處,還請看到的小夥伴多多指教,先行謝過react

如下↓webpack

項目簡介

react-admin 是由 create-react-app 腳手架快速構建,基於 React 生態系統搭建的後臺管理系統模板。實現了登錄/註銷、路由懶加載、axios封裝、簡單權限管理等功能,它能夠幫助你快速生成管理系統模板,你只須要添加具體業務代碼便可ios

線上預覽地址 預覽地址git

GitHub 代碼 代碼地址es6

技術棧

此項目涉及的技術棧主要有 es6reactreact-routerreduxreact-reduxCreate React Appreact-loadableaxios等,因此你可能須要提早了解這些知識,這樣會對你瞭解這個項目有很大的幫助github

基本功能

  • 路由懶加載
  • 麪包屑導航
  • 經常使用 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 樣式按需加載
複製代碼

總體思路

打造一個任何一個項目,除去前期須要考慮的受衆羣體(其實就是兼容…)以外,再加上技術選型,以後就到了頁面架構層。在這個項目中,前期不在咱們的考慮範圍以內(已經肯定了有木有),因此就從頁面架構開始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,詳情請 參見這裏

最後

這個項目都是本人閒暇時間開發,主要是爲了熟悉 react 開發流程以及其周邊生態的使用,項目仍是比較簡陋,後期會進行迭代開發,將其打形成一個更加實用的後臺管理模板

若是以爲不錯或者對你有些許的幫助,歡迎 star,或者你有更好的實現方式、有趣的 idea,也歡迎留言交流

最後,推薦一下本人的前端學習歷程文檔,裏面分享了許多前端知識的小片斷 點擊這裏 進行查看, 歡迎 star 關注

相關文章
相關標籤/搜索