一、前言
鏈接:https://juejin.im/post/5d9b5ddee51d45781b63b8f7
本系列文章將介紹從零開始搭建一個高可複用的後臺架構系統,讓每一個人都能輕鬆搭出自己的後臺。系統功能包含登錄授權、路由鑑權與組件化,涉及react-router與react-redux的應用。系統最終實現的效果:
![「React系列」手把手帶你擼後臺系統(架構篇)](http://p2.ttnews.xyz/loading.gif)
- 本篇介紹的內容包含:腳手架環境搭建與構建配置說明
- 基礎架構設計
- 側邊欄實現
- 初探路由
二、React環境搭建
2.1 腳手架環境
創建一個React App,目測有3到4種方法,這是官網文檔的說明,可以移步查看。我們這裡主要介紹npx和npm兩種方式。
2.1.1 什麼是npx?
npx是[email protected]版本新增的命令,如果你的npm版本是高於這個版本的,那麼你可以直接使用npx命令了;否則就需要全局安裝:npm install -g npx。但個人建議是升級npm:npm install -g npm@next。
npx的願景就是:讓天下沒有難調用的模塊。譬如我們項目中集成了Eslint模塊,我們想要在命令行調用該模塊必須像這樣:./node_modules/.bin/eslint --init;有了npx,我們可以直接:npx eslint --init。
同時,**npx還能避免全局安裝,真正的屬於用完即走。**早先以前,我們使用create-react-app創建react應用,必須要全局安裝該模塊,react>
使用npx創建本後臺應用:npx create-react-app my-admin
2.1.2 npm init經歷了什麼?
在@5.2版本之前,執行npm init會在當前目錄創建一個package.json文件,而在@5.2及之後的版本,我們可以通過npm init <initializer>命令創建一個應用,譬如:npm init react-app my-app。其本質也是內部調用npx命令,會自動將react-app補全為create-react-app,繼而下載並創建應用。/<initializer>
使用npm init創建本後臺應用:npm init react-app my-admin
2.2 構建配置
在上一步驟創建應用之後,默認的目錄架構像下面這樣:
![「React系列」手把手帶你擼後臺系統(架構篇)](http://p2.ttnews.xyz/loading.gif)
執行npm eject命令會生成config和script兩個目錄,目錄下是與項目相關的webpack配置文件,我們可以根據需要自定義構建配置。運行命令後的目錄架構如下:
其次,我們還可以在一些特殊文件中定義配置信息:新建jsconfig.json文件,並填充如下內容:
{
"compilerOptions": {
"baseUrl": "src" // 編譯根路徑
}
}
複製代碼
2.3 目錄說明
最終我們系統的目錄架構如下:
├── config // 配置相關
├──/>├── public // 應用對外目錄
├── src // 源代碼
│ ├── components // 公共組建
│ ├── font // 字體相關
│ ├── js // js庫
│ ├── router // 路由相關
│ ├── scss // 樣式表
│ ├── store // redux相關
│ ├── views // 頁面應用
│ ├── index.js // 入口文件
│ ├── Page.js // 頁面路由入口
│ ├── App.js // 主應用入口
複製代碼
三、基礎架構
從主應用入口文件·App.js·開始,根據我們系統後臺的佈局,用以下代碼替換原有代碼:
import React, { Component } from 'react'
class App extends Component {
render () {
return (
側邊導航欄
/
<header>
Hi, 安歌
/<header>
主體內容
<footer>
Copyright@2019 安歌
/<footer>
/
)
}
}
export default App
複製代碼
加上樣式:在scss目錄新建index.scss,同時刪除根目錄的App.css文件(根據個人習慣選擇scss或其他的預編譯語言),並在index.js中添加樣式表的引用。可以得到如下效果:
四、側邊導航欄
4.1 在components目錄新建SideBar.js組件,同時在router目錄下新建路由配置文件config.js,這份配置文件由側邊欄跟路由共用:
// Sidebar.js: 側欄導航組件,側欄菜單在router/config.js配置
import React, { Component } from 'react'
class SideBar extends Component {
constructor (props) {
super(props)
this.state = {
routes: [] // 路由列表
}
}
render () {
return (
側邊欄
)
}
}
export default SideBar
複製代碼
4.2 定義路由配置文件:支持多級嵌套,routes與component不能同級共存,如果存在子菜單,則用routes字段,否則使用component字段。
// router/config.js
export default [
{
title: '我的事務', // 頁面標題&一級nav標題
icon: 'icon-home',
routes: [{
name: '待審批', // 次級nav標題
path: '/front/approval/undo', // 路由url
component: 'ApprovalUndo' // 路由組件
}, {
name: '已處理',
path: '/front/approval/done',
auth: 'add', // 訪問所需權限
component: 'ApprovalDone'
}]
},
// ...
]
複製代碼
4.3 使用遞歸渲染側邊欄
// SideBar.js
import React, { Component, Fragment } from 'react'
class SideBar extends Component {
constructor (props) {
// ...
this.generateSidebar = this.generateSidebar.bind(this)
}
render () {
return ( // 渲染側邊欄
{ map(this.generateSidebar, this.state.routes) }
)
}
generateSidebar (item) { // 一級nav
return
'on': item.active /* 當前菜單展開/收起標識 */
}) }>
{ item.title }
{ this.generateSubMenu(item.routes) }
}
generateSubMenu (routes) { // 子級nav
return map(each =>
{ each.component ? : (
<fragment>
'on': each.active // 當前菜單展開/收起標識
}) }>
{ each.name }
{ this.generateSubMenu(each.routes) }
/<fragment>
) }
}
}
複製代碼
加上樣式之後如下圖:
- SideBar.js中預留兩個API留待下篇解說:checkActive: 根據當前訪問路由檢測菜單項的收合狀態;
- hasPer: 檢測當前用戶是否有菜單項的訪問權限,有則渲染,無則跳過渲染;
五、基礎路由
我們需要藉助react-router實現幾個頁面:登錄、404以及權限錯誤。這些屬於頁面級路由,可以歸在views目錄下,這裡我們在views目錄新建login目錄作為登錄應用、components目錄下新建404.js和AuthError.js,將其視作組件:
├── components // 公共組件
│ ├── 404.js // not found
│ ├── AuthError.js // permission error
├── views // 公共組件
│ ├── login // 登錄應用
│ │ ├── index.js // 登錄頁面入口
複製代碼
Page.js註冊路由:
// Page.js
import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom'
import App from 'App'
import AsyncComponent from 'components/AsyncComponent'
const Login = AsyncComponent(() => import(/* webpackChunkName: "login" */ 'views/login'))
const NotFound = AsyncComponent(() => import(/* webpackChunkName: "404" */ 'components/404'))
const AuthError = AsyncComponent(() => import(/* webpackChunkName: "autherror" */ 'components/AuthError'))
class Page extends Component {
render () {
return (
<router>
<switch>
<route> <redirect> } />
<route>
<route>
<route> {
const isLogin = false // 登錄狀態從redux獲取
return isLogin ? <redirect> : <login>
} } />
<route>} />
/<route>/<route>/<route>/<switch>
/<router>
)
}
}
export default Page
複製代碼
Route接口用於註冊路由並定義渲染邏輯,Page.js引入了主應用App.js文件,因此主入口文件index.js需要引入Page.js並渲染(將原來的引入App.js改成Page.js即可)。
六、異步組件
在上一章節基礎路由部分我們用到了一個組件函數AsyncComponent,它是一個工廠函數,用於異步解析我們的組件定義。在大型應用中,我們會使用很多的異步組件,譬如() => import(/* webpackChunkName: "login" */ 'views/login')是一個異步組件,它返回一個promise,在React裡面,我們需要自己寫一個工廠函數來解析,讓Promise變為React可用的組件。AsyncComponent的邏輯很簡單:異步解析和渲染:
// AsyncComponent.js
import React, { Component } from 'react'
export default function asyncComponent (importComp) {
class AsyncComponent extends Component {
constructor (props) {
super(props)
this.state = {
component: null
}
}
async componentDidMount () {
const { default: component } = await importComp()
this.setState({
component: component
})
}
render () {
const C = this.state.component
return C ?: null
}
}
return AsyncComponent
}
複製代碼
如上一頓操作之後,我們訪問/front/login、/front/404、/front/autherror就能分別訪問到登錄、Page Not Found和Permission Error頁面了,訪問根路徑會被重定向到/front/approval/undo。
閱讀更多 月漫灣 的文章