手把手帶你搭建React16+Router+Redux-saga+Antd後臺管理系統

React 項目

目錄

1、技術棧

2、項目安裝

3、項目架構

4、測試

5、部署

6、首頁入口

7、導航菜單

8、Redux-saga狀態共享

9、Antd配置

10、結尾

11、Github連接


1、技術棧

詳情可參閱 package.jsonjavascript

  • Create-react-app 3.0+
  • Yarn
  • React 16.9.0
  • Redux-saga
  • React Router
  • Less
  • Axios 請求庫
  • Webpack 4.0+
  • ES6 + Babel
  • Antd @3.23.6

項目說明:css

1.路由懶加載vue

2.錯誤路由匹配404頁面java

3.Api請求封裝Axios工具類react

4.redux-saga處理異步請求webpack

5.nprogress加載條ios

6.路由鑑權git

7.裝飾器模式狀態共享、表單包裹github

8.在redux中加入路由跳轉功能web

7.接口是java服務端的Api,暫時就不抽出來了,因此項目是沒法正常登錄的,項目會提供UI視覺稿,有對應需求的能夠借閱參考。最後說一句,redux-saga真香~~~


2、項目安裝

本項目用的是yarn管理依賴包,須要安裝yarn

yarn install //安裝依賴

yarn start //運行
複製代碼

3、項目架構

⊙ 目錄結構

.
├─ config/            # Webpack 配置目錄
├─ public/             # 模板文件
├─ dist/             # build 生成的生產環境下的項目
├─ scripts/             # Webpack環境變量配置
├─ src/              # 源碼目錄(開發都在這裏進行)
│   ├─ assets/         # 放置須要經由 Webpack 處理的靜態文件
│   ├─ components/     # 組件
│   │   ├─ Layout/       # 全局佈局
│   │   ├─ PrivateRoute/ # 路由守衛
│   ├─ store/            # Redux-sagas
│   │   ├─ actions/      # (Actions)
│   │   ├─ reducers/     # (Reducers)
│   │   ├─ sagas/        # (Sagas)
│   │   ├─ index.js      # (Store文件管理)
│   ├── router/          # 路由(ROUTE)
│   ├── service/         # 服務(SERVICE,統一Api管理)
│   ├── utils/           # 工具庫
│   ├── pages/           # 視圖頁(pages)
│   ├── index.js         # 啓動文件
│   ├── App.js           # 主入口頁
├── .gitignore       # (配置)需被 Git 忽略的文件(夾)
├── package.json      
複製代碼

4、測試

暫未加入測試工具


5、部署

yarn build

build以後,若是想要在本地線上環境打開的話,建議先安裝一個http-server本地服務器

npm install http-server -g

安裝成功以後,直接在要訪問的build文件夾中運行http-server命令便可打開本地服務環境。也可自行配置端口,具體命令可參考http-server

打包後路徑問題致使頁面空白

在build目錄下開啓本地服務器後,有可能打開本項目是空白的,資源加載不出來,只要在package.json裏面配置homepage屬性就行了

//package.json 文件增長配置
"homepage": ".",
複製代碼

6、首頁入口

入口文件主要定義路由頁面,由於是此項目是單頁面應用,因此主入口只要配置三大模塊路由便可,根路由地址/匹配<IndexLayout />,login匹配<Login />,404匹配<ErrorPage />

爲了讓路由懶加載,引入路由的時候能夠用異步組件包裹一層Component,爲了方便加載時查找到對應的文件名,能夠設置/* webpackChunkName: "name" */便可,以下

// 該文件爲實現相似github頁面加載的那個加載條
import LoadableComponent from '@/utils/LoadableComponent'
const Login = LoadableComponent(()=>import(/* webpackChunkName: "login" */ '@/pages/Login'))
複製代碼

這裏引用的是HashRouter路由模式,BrowserRouter模式須要後臺配合,不然打包的時候在當前路由地址刷新時會形成空白的錯誤。

入口文件App.js的所有代碼貼一下吧

import React, { Component } from 'react'
import IndexLayout from '@/components/Layout/index'
import { connect } from 'react-redux';
import LoadableComponent from '@/utils/LoadableComponent'
import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'

const Login = LoadableComponent(()=>import(/* webpackChunkName: "login" */ '@/pages/Login'))
const ErrorPage = LoadableComponent(()=>import(/* webpackChunkName: "errorPage" */ '@/pages/ErrorPage'))

//裝飾器模式連接Redux數據,省略不少複雜代碼,真香。注意:不要的對象能夠傳個null
@connect(
    state => ({
        id_token: state.loginReducer.id_token
    })
)
class App extends Component {
    render() {
        return (
            <Router>
                <Switch>
                    //此處重定向的地址爲登陸後的首頁面地址
                    <Route exact path="/" render={ () => <Redirect to="/apply" push /> } />
                    
                    //此處404頁面只是聲明路由地址,暫未匹配路由
                    <Route path="/404" component={ ErrorPage } />
                    
                    //路由鑑權,若是沒有id_token則跳轉到登陸頁
                    <Route path="/login" render={() => {
                        return this.props.id_token ?  <Redirect to="/" /> : <Login />
                    }} />
                    
                    //登陸後的主模板組件
                    <Route render={ () => <IndexLayout /> } />
                </Switch>
            </Router>
        )
    }
}

export default App

複製代碼

6、導航菜單

該項目爲左右佈局,沒有Header,Footer。界面比較創新...

import React, { Component } from 'react'
import ContentMain from '@/components/Layout/ContentMain' //主內容組件
import SliderNav from  '@/components/Layout/SliderNav' //菜單欄組件
import { Layout } from 'antd'

const { Content, Sider } = Layout;

class IndexLayout extends Component {

    render() {
        return (
            <Layout>
                <Sider
                    collapsible
                    trigger={null}
                >
                    <SliderNav/>
                </Sider>
                <Layout>
                    <Content style={{background: '#f7f7f7'}}>
                        <ContentMain/>
                    </Content>
                </Layout>
            </Layout>
        )
    }
}

export default IndexLayout
複製代碼

菜單欄的代碼就不一一張貼了,內容有點長,菜單欄的路由欄目有留子菜單的入口配置,只要對應的路由數組按照格式配置好便可。全部的導航菜單佈局Components組件庫中的Layout文件夾目錄下。這裏提供一下導航欄的主路由配置信息。注意,這裏的路由配置跟以前的路由配置不是同一個信息,前者是總路由地址,這裏的路由是右邊Content的全部路由信息。

import React, { Component } from 'react'
import { withRouter, Switch, Redirect, Route } from 'react-router-dom'
import LoadableComponent from '@/utils/LoadableComponent'
//路由鑑權組件,包裹全部`Content`的頁面,若是token失效,則跳回`Login頁面`
import PrivateRoute from '@/components/PrivateRoute'

const Apply = LoadableComponent(()=>import(/* webpackChunkName: "apply" */ '@/pages/Apply'))
const Case = LoadableComponent(()=>import(/* webpackChunkName: "case" */ '@/pages/Case'))

@withRouter
class ContentMain extends Component {
    render () {
        return (
            <div style={{padding: '20px 32px'}}>
                <Switch>
                    <PrivateRoute exact path='/apply' component={ Apply }/>
                    <PrivateRoute exact path='/case' component={ Case }/>
                    
                    //404頁面在這裏匹配路由,就能正確匹配錯誤路由了
                    <Route render={ () => <Redirect to="/404" /> } />
                    <Redirect exact from='/' to='/apply'/>
                </Switch>
            </div>
        )
    }
}

export default ContentMain

複製代碼

8、Redux-saga狀態共享

在決定用Redux-saga以前,也有考慮引用Redux-thunk來作狀態共享,畢竟Redux-thunk上手要簡單不少。可是Redux-thunk同步異步代碼都要下在同一個文件裏面,若是單頁面接口過多的話,會形成麪條式的代碼,也不利於理解和維護。因此毅然決定引用Redux-saga。中間碰到不少的坑,由於在Github上不多能發現Redux-saga比較完整系列能借閱的項目,每次卡住都是各類翻閱資料。如下會羅列出在開發過程當中容易遇到的問題,讓參閱本項目的朋友們能少踩點坑...

1.先直接貼store的配置文件吧

import { createStore, applyMiddleware, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducers';
import sagas from './sagas'
import { routerMiddleware } from 'react-router-redux';

const sagaMiddleware = createSagaMiddleware();
const createHistory = require('history').createHashHistory;
const history = createHistory();   // 初始化history
const routerWare = routerMiddleware(history);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware, routerWare));
const store = createStore(
	reducer,
	enhancer
)

sagaMiddleware.run(sagas)

export default store
複製代碼

這裏引入的sagas是按頁面引用分類的saga文件,避免全部異步請求都寫在同一個文件中。由於本項目只有三大模塊,登陸頁、申請頁、案件頁,因此分三大模塊就行了。

// saga模塊化引入
import { fork, all } from 'redux-saga/effects'

// 異步邏輯
import { loginSagas } from './login'
import { applySagas } from './apply'
import { caseSagas } from './case'

// 單一進入點,一次啓動全部Saga
export default function* rootSaga() {
    yield all([
        fork(loginSagas),
        fork(applySagas),
        fork(caseSagas)
    ])
}
複製代碼

redux-saga的正確使用在這裏我就不作過多闡述了,想要引用的同窗能夠去看看官方文檔的介紹。或者借閱本項目的源代碼,依葫蘆畫瓢,多寫幾遍天然就多會了。不過,在此的前提是,你首先得去了解一遍Es6的Generator函數

具體的redux-saga異步引用在項目中不少地方有應用,等時間充裕了我補一遍redux-saga的使用教程。

這裏說兩個引用redux-saga容易遇到的坑

  • 異步請求結束後跳轉路由
  • redux-saga異步請求只執行一次,好比分頁接口,你怎麼切分頁都只加載第一次

① 異步請求結束後跳轉路由

reactvue的路由跳轉有點不一致,vue是封裝了全部的路由信息,只要你引用了vue-router,你就可爲所欲爲的引用路由跳轉。可是react不同,在js文件中若是引用了react-router後能夠直接引用<Link></Link>或者this.props.history.push('/login')的方式跳轉路由。可是引用了redux-saga狀態共享後,當異步請求結束以後再跳轉路由信息是再正常不過的需求了,但這裏this.props.history.push('/login')這種方式是不生效的,想要在狀態共享中跳轉路由,須要額外配置。

1.首先安裝historyreact-router-redux

yarn add history react-router-redux

2.在store裏面引用

import createSagaMiddleware from 'redux-saga';
import { routerMiddleware } from 'react-router-redux';

const sagaMiddleware = createSagaMiddleware();
const createHistory = require('history').createHashHistory;
const history = createHistory(); //初始化history
const routerWare = routerMiddleware(history);

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
//這裏是包裹兩個中間件,saga和狀態共享路由的中間件,想要在saga中跳轉頁面,routerWare這個中間件是必不可少的!!!
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware, routerWare));
複製代碼

3.在saga中應用

import { push } from 'react-router-redux';
function* login() {
  if (...) {// 登陸成功,路由跳轉
    yield put(push('/login')) //Generator指令跳轉
  }
}
複製代碼

②redux-saga異步請求只執行一次

其實形成這種緣由最主要仍是出於對Generator指令不熟悉的緣故 以下代碼,每一個Generator方法內都要加一個while(true){},配合多個Generator方法yield all([])監聽,這樣就能作到每次一個saga任務請求結束後,下次再進來(例如分頁)同一個請求時,每次都會從新監聽,而後再繼續進入Generator函數內,這樣就不會一個接口就只請求一次。

這個細微的小Bug真的是讓我吃了不小的苦頭啊...

function * getSearchRequest() {
    while(true){ //保持監聽鏈接
        const resData = yield take(types.GET_SEARCH_DATA);
        const response = yield call(seachData, resData.payload)
        yield put(getSearchDataSuccess(response))
    }
}

function * getDetailRequest() {
    while(true){
        const resData = yield take(types.GET_DRAFT_DETAIL_REQUEST);
        const response = yield call(searchDetail, resData.payload)
        yield put(getDetailSuccess(response));
    }
}

export function * caseSagas() {
    yield all([
        fork(getSearchRequest),
        fork(getDetailRequest)
    ]);
}

複製代碼

antd

9、Antd配置

本項目引用的是Antd的UI組件庫,一次性打包加載全部的組件固然是過於臃腫,官網給的按需加載配置建議是在項目yarn run eject以前的,明顯不符合絕大多數線上代碼的一個定製化配置,因此要配置antd按需加載,還得另行配置

這裏我直接把babel全部的配置都放上吧,解決兩個知識點。antd按需加載和裝飾器模式配置。

裝飾器模式要先安裝依賴,而後配置babel

yarn add babel-plugin-transform-decorators-legacy

//package.json
"babel": {
    "plugins": [
      [
        "@babel/plugin-proposal-decorators", //引用@connect、@withRouter裝飾器模式必須配置babel
        {
          "legacy": true
        }
      ],
      [
        "import", //antd按需加載配置
        {
          "libraryName": "antd",
          "libraryDirectory": "es",
          "style": true //這裏若是設置爲true的話則爲自定義主題,不然就是加載所有antd css樣式
        }
      ]
    ]
  }
複製代碼

如上,若是antd按需加載配置的style屬性爲true的話,那自定義主題配置可還沒結束,繼續...

webpack.config.js文件中,

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
    const loaders = ...
    if (preProcessor) {
        let loader = {
            loader: require.resolve(preProcessor),
            options: {
                sourceMap: true,
            },
        }
        if (preProcessor === "less-loader") {
            //如下爲antd的全部主題配色,更多的變量可訪問
            //https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
            loader.options.modifyVars = {
                'primary-color': '#C89F64', //主題顏色
                'link-color': '#1DA57A', //主題link顏色
                'border-radius-base': '2px'
            }
            loader.options.javascriptEnabled = true
        }
        loaders.push(
            {
                loader: require.resolve('resolve-url-loader'),
                options: {
                    sourceMap: isEnvProduction && shouldUseSourceMap,
                },
            },
            loader
        );
    }
    return loaders;
};
複製代碼

10、結尾

整個項目實際上是已經開發完畢的,惟一的遺憾是API請求沒法開放,只能提供參考。 如下我會附一個Github的源碼,包括設計稿,拿着UI看源碼就不那麼費勁了。若是遇到同模塊能引用的,it`s my pleasure~~~

附:React是徹底的組件化開發概念,萬物皆組件,在作這個項目時,時間太緊,這個項目就花了六七個工做日,不包括測試時間。因此代碼耦合仍是有點嚴重的,見諒哈~ 等後期時間充裕了會再加上Immutable.js,並結合React-hooks優化部分模塊,讓它更加有逼格些...嘿哈。

最後,未完待續。。。


11、Github連接

github.com/zengxiaozen…

相關文章
相關標籤/搜索