使用 React 全家桶搭建一個後臺管理系統

牧云云發表於2017-06-14

使用 React 全家桶搭建一個後臺管理系統

本文首發在我的個人部落格:muyunyun.cn/posts/9bfbd…

使用React技術棧搭建一個後臺管理系統最初是為了上手公司的業務,後來發現這個專案還能把平時遇到的有趣的demo給整合進去。此文嘗試對相關的技術棧以及如何在該專案中引人Redux進行分析。

專案地址以及區域性展示

  • 專案地址

  • 小模組展示:

    使用 React 全家桶搭建一個後臺管理系統

  • redux在專案中的運用demo展示
    使用 React 全家桶搭建一個後臺管理系統

專案目錄結構

├── build.js                   專案打包後的檔案
├── config                     webpack配置檔案
│   ├──...
│   ├──webpack.config.dev.js   開發環境配置
│   ├──webpack.config.prod.js  生產環境配置
├── node_modules               node模組目錄
├── public
│   └──index.html
├── scripts
│   ├── build.js               打包專案檔案
│   ├── start.js               啟動專案檔案
│   └── test.js                測試專案檔案
├── src
│   ├── client                 匯聚(入口)目錄
│   ├── common                 核心目錄
│   │   ├── actions            redux中的action
│   │   ├── components         通用功能元件
│   │   ├── container          通用樣式元件
│   │   ├── images
│   │   ├── pages              頁面模組
│   │   ├── reducers           redux中的reducer
│   │   ├── utils              工具類
│   │   │   ├── config.js      通用配置
│   │   │   ├── menu.js        選單配置
│   │   │   └── ajax.js        ajax模組(日後用到)
│   │   └── routes.js          前端路由
│   └── server                 服務端目錄(日後用到)
│       └── controller
├── .gitignore
├── package.json
├── README.md
└── yarn.lock複製程式碼

專案的初始結構和構造原因已羅列如上,由於過些日子會引人ts,所以專案結構必然還會改動,但肯定基於這基本雛形擴充套件的。

下面對目錄結構作以下說明

  • 專案最初始是用 create-react-app 初始化的,create-react-app 是Facebook官方提供的 React 腳手架,也是業界最優秀的 React 應用開發工具之一;
  • client 作為入口目錄,到時候可以把第三方中介軟體也放在此處;
  • container 和 components 存放的都是 react 元件,區別如下表。但是我把和樣式有關的元件就放在container中,把和功能有關的模組(比如自己分裝的表格元件、彈出輸入框元件等)就放到components中,若日後有需要,container 和 component 元件都是可以在 Redux 資料流中的。
container component
目的 如何工作(資料獲取,狀態更新) 如何顯示(樣式,佈局)
是否在 Redux 資料流中
讀取資料 從 Redux 獲取 state 從 props 獲取資料
修改資料 向 Redux 派發 actions 從 props 呼叫回撥函式
實現方式 向react-redux生成 手寫
  • ajax 模組到時候計劃用 fetch 封裝一個ajax,感覺使用 fetch 還是蠻便利的。
  • server 層就是作為閘道器層,日後計劃用來寫 node 的。

技術棧相關

雖然用到的技術棧眾多,但是自己也談不上熟練運用,多半是邊查API邊用的,所以只羅列些自己用相關的技術棧解決的點;

webpack(2.x)

4月的時候 create-react-app 還是基於 webpack(1.x) 構建的,5月27號升到了webpack(2.6),於是我也進行了 webpack 的版本升級。

按需載入

babel-plugin-import 是一個用於按需載入元件程式碼和樣式的 babel 外掛,使用此外掛後,在引人 antd 相應模組就能實現按需引人,在config/webpack.config.dev.js 檔案中作如下修改:

{
        test: /\.(js|jsx)$/,
        include: paths.appSrc,
        loader: require.resolve('babel-loader'),
        options: {
          plugins: [
            "transform-decorators-legacy",  // 引人 ES7 的裝飾器 @
            ['import', [{ libraryName: 'antd', style: true }]],
          ],
          cacheDirectory: true,
        },
      },複製程式碼

引人less

首先引人 less-loader 來載入 less 樣式,同時修改 config/webpack.config.dev.js 檔案


        test: /\.less$/,
        use: [
          require.resolve('style-loader'),
          require.resolve('css-loader'),
          {
            loader: require.resolve('postcss-loader'),
            options: {
              ident: 'postcss', //https://webpack.js.org/guides/migrating/#complex-options
              plugins: () => [
                require('postcss-flexbugs-fixes'),
                autoprefixer({
                  browsers: [
                    '>1%',
                    'last 4 versions',
                    'Firefox ESR',
                    'not ie < 9', // React doesn't support IE8 anyway
                  ],
                  flexbox: 'no-2009',
                }),
              ],
            },
          },
          {
            loader: require.resolve('less-loader'),
            options: {
              modifyVars: { "@primary-color": "#1DA57A" },  // 這裡利用了 less-loader 的 modifyVars 來進行主題配置, 變數和其他配置方式可以參考 [配置主題](https://user-gold-cdn.xitu.io/2017/6/15/e8ba356d7b10cec196d48159e41b6e6e) 文件。
            },
          },
        ],
      },複製程式碼

一鍵釋出到 gh-pages

用到了 gh-pages ,使用 npm run deploy 一鍵釋出到自己的gh-pages上,姑且把gh-pages當成生產環境吧,所以在修改config/webpack.config.dev.js 檔案的同時也要對 config/webpack.config.prod.js 作出一模一樣的修改。

引用路徑的縮寫

alias: {
      'react-native': 'react-native-web',
      components: path.resolve(__dirname, '..') + '/src/common/components',
      container: path.resolve(__dirname, '..') + '/src/common/container',
      images: path.resolve(__dirname, '..') + '/src/common/images',
      pages: path.resolve(__dirname, '..') + '/src/common/pages',
      utils: path.resolve(__dirname, '..') + '/src/common/utils',
      data: path.resolve(__dirname, '..') + '/src/server/data',
      actions: path.resolve(__dirname, '..') + '/src/common/actions',
      reducers: path.resolve(__dirname, '..') + '/src/common/reducers',
    },複製程式碼

配置了引用路徑的縮寫後,就可以在任意地方如這樣引用,比如

import Table from 'components/table'複製程式碼

Antd(2.x)

antd是(螞蟻金服體驗技術部)經過大量的專案實踐和總結,沉澱出的一箇中臺設計語言 Ant Design,使用者包括螞蟻金服、阿里巴巴、口碑、美團、滴滴等一系列知名公司,而且我從他們的設計理念也學到了很多關於UI、UX的知識。
該專案採用的是antd最新的版本2.10.0,由於2.x的版本和1.x的版本還是相差蠻大的,之前參考的專案(基於1.x)改起來太費勁,所以在元件那塊就乾脆自己重新封裝了一遍。這部分知識點建議多看文件,官方更新還是非常勤快的。

React-router(4.x)

react-router 4.x和2.x的差異又是特別的大,召喚文件,網上基本上都還是2.x的教程,看過文件之後,反正簡而言之其就是要讓使用者更容易上手。印象最深的是以前巢狀路由寫法在4.x中寫到同層了。如下示例他們的效果是相同的。

2.x:

<Route path="/" component={App}>
    <Route path="/aaaa" component={AAAA} />
    <Route path="/bbbb" component={BBBB} />
</Route>複製程式碼

4.x:

<Route path="/" component={App} />
<Route path="/aaaa" component={AAAA} />
<Route path="/bbbb" component={BBBB} />複製程式碼

Fetch

fetch 使用比較簡單,基本的 promise 用法如下

fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))複製程式碼

此外還能這樣用

try {
  let response = await fetch(url);
  let data = await response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}複製程式碼

但是其簡潔的特點是為了讓我們可以自定義其擴充套件,還是其本身就還不完善呢?我在呼叫 JSONP 的請求時,發現其不支援對 JSONP 的呼叫,所幸社群還是很給力地找到了 fetch-jsonp 這個模組,實現了對百度音樂介面呼叫。fetch-jsonp使用也和 fetch 類似,程式碼如下

fetchJsonp(url,{method: 'GET'})
  .then((res) =>res.json())
  .then((data) => {})複製程式碼

Redux

使用了redux也已經有段時日了,我對redux的定義就是更好的管理元件的狀態,一旦應用的邏輯複雜起來,各種元件狀態、介面耦合起來,就容易出岔子,redux就是為了解決這個而誕生的,讓我們可以更多地關注UI層,而降低對狀態的關注。

使用 React 全家桶搭建一個後臺管理系統

畫了一幅比較簡陋的圖來說明 redux 的大致流程,假設首先通過滑鼠點選頁面上的按鈕觸發了一個行為(action),這時我們叫了一輛計程車 dispatch() 將這個 action 帶到了終點站 store。這時候 store 就會通過 reducer 函式返回一個新的狀態 state,從而改變 UI 顯示。之前也寫了篇深入Redux架構

下面通過把 代辦事項 這個demo運用到後臺管理系統中來講解 Redux 在其中的運用。

首先,在入口目錄建立 store

const store = createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    { routes }
  </Provider>,
  document.getElementById('root')
);複製程式碼

接著,我使用了 redux-actions 這個模組。使用 redux-actions 的好處是能簡化大量對 action 的宣告,以及能簡化 reducer 的寫法。

代辦事項的 actions 檔案片段(拿展示全部任務、已完成任務、未完成任務的 action 舉例):

import { createAction } from 'redux-actions'

export const setVisibility = createAction('SET_VISIBILITY')複製程式碼

沒使用 redux-actions 時,actions 寫法如下,可看出著實麻煩了不少,

export const setVisibility = (filter) => {
    return {
        type: "SET_VISIBILITY",
        filter
    }
}複製程式碼

相應的代辦事項的 reducers 檔案片段:

export const setVisibility = handleActions({
  'SET_VISIBILITY'(state, action) {
    return { ...state, ...action.payload}
  }
}, 'SHOW_ALL')複製程式碼

使用 redux-actions 後,只要進行如下呼叫,reducers檔案裡的SET_VISIBILITY的 action 就能捕獲到SHOW_ALL這個狀態。

import { setVisibility } from 'actions/todoList'
@connect(
    (state) => ({
        setVisibility: state.setVisibility, // 這個 setVisibility 是取自 reducers 的
    })
)

dispatch(this.props.dispatch(setVisibility('SHOW_ALL')))複製程式碼

connect 來自 react-redux,這裡的 @ 是 ES7裡的裝飾器的用法,使用它之後又能減少不少的程式碼量,原來還要寫 mapStateToPropsmapDispatchToProps

專案的一些擴充套件計劃

計劃在該專案把平時工作、學習中遇到的react案例抽離成demo展現出來,所以以後還會多出一些模組。另外過段時間會在該專案中引人 typescript,如果還有精力的話,可以在這個專案上折騰下閘道器層。喜歡這個專案的話,點我 Star

相關文章