一、論管理系統的模組化
不管是給別人做專案,還是自己家的專案,一個網站、app總少不了一套管理系統。
管理系統是個很特殊的東西,功能很多,對介面要求不高,可複用性特別好,尤其適合做成模組化的框架來使用。
下面是一個典型的管理系統介面:
【圖一、管理系統介面】
在圖上已經圈出了管理系統的幾個大模組:
- 使用者資訊、logo等所在的頂部資訊欄
- 左側的導航欄
- 主要展示區
根據React的思想,將整個網站視為一個app,現在三個大元件就很明確了。整個系統的流程就應當是點選導航欄之後,更換主要展示區所載入的元件實現不同的功能模組。
接下來就是進一步的細化主展示區的模組
【圖二、主展示區模組示意圖】
管理系統的資料展示也就是幾種主要方式,每次寫起來都要複製貼上一大堆的html程式碼導致很麻煩。進行模組化的處理之後,可以將主要精力放在對資料的處理上,而忽略介面的實現細節。大幅度提升之後的工作效率。
二、相關技術
與之前粗淺的React.js入門知識比起來,這次實踐中瞭解到了以下相關技術:
- Javascript ES6
- Webpack
- React-router
隨著ES6標準的日益流行,現在前端的應用也愈發廣泛。相比起之前的javascript來說,ES6標準更趨近於java的寫法,對於我這種後端程式猿更為友好和親切。
Webpack一直是React優先選擇的前端模組化工具之一。可以將js、css甚至是圖片資源打包到同一個js檔案中。一來方便控制,二來通過減少網路請求次數以提高網站的載入速度。
React-router是React官方的也是目前唯一可用的路由庫。通過此庫即可實現“點選導航連更換主展示區元件”的路由功能。
三、編寫元件
1 總體框架
廢話不多說,直接上程式碼:
import React from 'react';
import ReactDOM from 'react/lib/ReactDOM';
import { Router, Route, hashHistory,Link } from 'react-router';
import Topnav from './topnav.js';
import Navbar from './navbar.js';
import {EditNormalUser,NormalUser,AdminUser,AddAdmin} from './userComponent.js';
class IndexWrapper extends React.Component {
render () {
return (
<div className="wrapper preload">
<header className="top-nav" id="top-nav">
<Topnav />
</header>
<aside className="sidebar-menu fixed" id="navbar">
<Navbar />
</aside>
<div className="main-container" id="main">
{this.props.children}
</div>
</div>
)
}
}
class Main extends React.Component{
render(){
var router = (
<Router history={hashHistory} >
<Route path="/" component={IndexWrapper}>
<Route path="/normal" component={NormalUser}/>
<Route path="/admin" component={AdminUser}/>
<Route path="/admin/add" component={AddAdmin}/>
<Route path="/normal/edit/:uid" component={EditNormalUser}/>
</Route>
</Router>
);
return router;
}
};
複製程式碼
Router與Route就是兩個屬於React-router庫的元件。是實現路由功能的關鍵。 hashHistory是用來維護瀏覽器history的方式,另一種實現是browserHistory。 對應於/normal路徑,hashHistory的方式生成的URL如下:
http://localhost:8080/admin.html#/normal?_k=bptt31
複製程式碼
browserHistory則會跳轉到如下路徑:
http://localhost:8080/normal
複製程式碼
相比之下,hashHistory的方式對後端程式碼影響較小,故在此選擇了hashHistory。
Router維護了一個路由的上下文。根據約定呢,在Router下只能有一個元素,在此對應的就是根路徑的路由"/",對應的元件則是IndexWrapper,對整體框架的簡單包裝。
在React中,有個很重要的屬性:this.props.children,代表此元素中的子元素。在Main元件中可見對於根路徑的路由,對應的元件是IndexWrapper,其中除了頂部的Topnav和左側的Navbar元件之外,就使用了this.props.children屬性,根據路由載入不同的主展示區元件使用。
2、導航
本系統內的導航欄設計為最多二層的結構。包括兩種元件:
- 單層的導航欄SingleNav
- 雙層的導航欄MultiNav
在資料上,使用陣列來進行填充,樣例如下:
navtree : [
{
name:"首頁",
component:"./home"
},
{
name:"使用者管理",
subnav:[
{
name:"普通使用者管理",
component:"/normal"
},{
name:"管理員管理",
component:"/admin"
},
]
}
]
複製程式碼
而對應於陣列的處理迴圈如下:
var navs = [];
for(var i=0;i<this.state.navtree.length;i++){
var nav = this.state.navtree[i];
if(!nav)
continue;
if(nav.subnav){
navs.push(<MultiNav key={"nav-multibar-"+i} nav={nav} ></MultiNav>);
}else{
navs.push(<SingleNav key={"nav-singlebar-"+i} nav={nav} ></SingleNav>);
}
}
複製程式碼
實際上呢,component指向的是路由的路徑,名稱什麼的是一個歷史遺留問題^_^
在導航這裡呢,又涉及到一個路由上很重要的元件:Link元件
<Link to={this.props.nav.component} activeClassName="active">
複製程式碼
Link元件會被編譯為一個a標籤,to則被編譯為href屬性,指向指定的url。同時解決了一個選中狀態的問題,當被選中時會自動新增類active,這也是前端常用的選中狀態類。
要注意的是,Link必須屬於某一個路由上下文中。也就是在Router元件之內。在最初的時候並沒有使用Router對整個頁面進行包裹,導致報錯提示Link不再RouterContext之中,編譯出href也為空。
3、介面UI元件案例
將UI元件化是節約日後工作量的重頭戲,下面就以一個簡單的例子來說明,此處並沒有什麼新的技術加入,所以直接上程式碼:
export class SmartBox extends React.Component{
render (){
var options = [];
if(this.props.option){
if(this.props.option.reload){
options.push(
<a href="#" className="widget-refresh-option" onClick={this.props.option.reload}>
<i className="fa fa-refresh"></i>
</a>
)
}
if(this.props.option.add){
if(typeof add == "function"){
options.push(
<a href="#" className="widget-refresh-option" onClick={this.props.option.add}>
<i className="fa fa-plus"></i>
</a>
)
}else{
options.push(
<Link to={this.props.option.add}>
<i className="fa fa-plus"></i>
</Link>
)
}
}
}
return (
<div className="padding-md">
{this.props.index}
<div className="smart-widget">
<div className="smart-widget-header">
{this.props.title}
<span className="smart-widget-option">
<span className="refresh-icon-animated">
<i className="fa fa-circle-o-notch fa-spin"></i>
</span>
{options}
</span>
</div>
<div className="smart-widget-inner">
<div className="smart-widget-body">
{this.props.children}
</div>
</div>
</div>
</div>
)
}
}
複製程式碼
這就是標準的管理系統主模組框架。包括了頂部的麵包屑導航和內容的外框包圍。兩個可選按鈕:重新整理,新增
此處同樣使用了this.props.children屬性來新增子節點。
元件的實際使用如下:
render (){
var index = (
<ul className="breadcrumb">
<li>使用者管理</li>
<li>管理員</li>
<li>列表</li>
</ul>
);
return (
<SmartBox title="使用者資訊列表" index={index}
option={{reload:this.loadPage.bind(this),add:"/admin/add"}}>
<DataTable index={this.state.index} data={this.state.data} buttons={this.state.buttons}/>
<Pager pagecount={this.state.pagecount} last={this.state.last} callback={this.loadPage.bind(this)} />
</SmartBox>
)
}
複製程式碼
在此案例中使用了SmartBox,DataTable和Pager三個UI元件,實現了一個標準的資料列表。
四、專案打包
最後的工作則是使用webpack對此專案進行預編譯和打包,在這裡需要安裝nodejs以使用npm工具。
首先要安裝webpack:
npm install webpack -g
複製程式碼
然後是對webpack進行配置,配置檔案如下:
var webpack = require('webpack')
module.exports = {
entry: './application.js',
output: {
filename: '../main.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: "babel",
query: {
presets: ['react', 'es2015']
}
}
]
}
}
複製程式碼
這個配置檔案嘛,他就做了三件微小的工作:
- 指定入口
- 指定輸出檔案
- 指定js檔案的載入器。
最後呢,我先執行了npm install,然後跑了webpack,就打包成了一個main.js檔案。
在html中只需要引入main.js檔案即可,無需再引入React.js相關的檔案。
當然如果願意,可以將網頁中所有的資源都用這種方式引入。
五、結束
一個簡單的web頁面模組化工作基本就此完成。
隨著現在web技術的飛快發展,前端的工程化、模組化已經是大勢所趨。不再像以前一樣隨便寫寫html和css,js,用幾行jquery就能完成工作了。
現在流行的元件化框架,包括react.js,angular.js,vue.js等,都是這種趨勢的產物。雖然作為一個主攻後端的工程師,對於前端技術必要的瞭解和掌握也是必不可少的。