終于,入職20天了。今天吃了烤串慶祝!
最近一周使用react框架做了一個(gè)具有簡(jiǎn)單功能的異步監(jiān)控平臺(tái),是接觸react之后做的第一個(gè)項(xiàng)目,簡(jiǎn)單總結(jié)一下。
一、開(kāi)發(fā)需求
一個(gè)異步監(jiān)控平臺(tái),需要顯示服務(wù)器的名稱、進(jìn)程名稱以及進(jìn)行一系列操作,包含:開(kāi)始、停止、重啟、顯示日志等,沒(méi)有設(shè)計(jì)圖,給出的參考圖如下:

而這個(gè)項(xiàng)目又有些特殊的要求,需要控制的項(xiàng)目也更多,需要有:機(jī)器、組、進(jìn)程這三個(gè)概念,因此,最終設(shè)計(jì)成了如下形式:

如圖,這是一臺(tái)機(jī)器中的三個(gè)進(jìn)程,分為兩個(gè)進(jìn)程組,每個(gè)組中對(duì)應(yīng)著不同的進(jìn)程,而這些進(jìn)程都需要進(jìn)行相應(yīng)的操作。使用react開(kāi)發(fā),可以將這些內(nèi)容分為組件,降低開(kāi)發(fā)難度。
二、開(kāi)發(fā)環(huán)境
本項(xiàng)目直接復(fù)用的組內(nèi)其他項(xiàng)目的結(jié)構(gòu)。主要基于通過(guò)對(duì)本部門(mén)業(yè)務(wù)項(xiàng)目的需要,在fis和webpack的基礎(chǔ)上集成了一套打包方式(內(nèi)部稱eden)。部分依賴如下,供參考:
"babel-core": "^6.5.2",
"babel-loader": "^6.2.3",
"babel-preset-stage-0": "^6.5.0",
"css-loader": "^0.26.1",
"eden-remote-deploy": "^0.0.1", //eden版本
"express": "^4.15.2",
... ...
"q": "^1.5.0",
"react-transform-hmr": "^1.0.4",
"serve-static": "1.12.2",
"style-loader": "^0.13.1",
"url-loader": "^0.5.8",
"webpack": "2.4.1",
"webpack-dev-middleware": "1.10.2",
"webpack-hot-middleware": "2.18.0",
"webpack-merge": "^4.1.0"
三、具體實(shí)現(xiàn)
1. react頁(yè)面結(jié)構(gòu)搭建
如開(kāi)發(fā)需求中提到的,組件化開(kāi)發(fā)可以降低開(kāi)發(fā)的難度,但同時(shí),在項(xiàng)目的開(kāi)始如何分配組件就顯得至關(guān)重要了。
根據(jù)項(xiàng)目的需求,我一開(kāi)始將這個(gè)app分成了四個(gè)組件,分別是:
Header:圖中的黑色條,項(xiàng)目的抬頭,暫時(shí)用來(lái)顯示標(biāo)題,日后可以拓展到功能鍵的顯示(擴(kuò)展了一個(gè)登入-登出的功能);
Server:圖中的深灰色框框,用來(lái)顯示每一臺(tái)機(jī)器的容器;
Title:圖中的淺灰色條條中的三個(gè)按鈕,用來(lái)控制一個(gè)進(jìn)程組的動(dòng)作;
Structure:圖中的表格部分,用來(lái)顯示每一組進(jìn)程的容器
在確定了相關(guān)組件之后,我開(kāi)始進(jìn)行組件開(kāi)發(fā),之所以考慮將Title組件單獨(dú)拿出來(lái),是為了盡量簡(jiǎn)化Structure這個(gè)組件,因?yàn)?,如果不這樣做,Structure組件需要同時(shí)控制組進(jìn)程和單個(gè)進(jìn)程的操作,邏輯比較復(fù)雜;但是實(shí)際開(kāi)發(fā)過(guò)程中,發(fā)現(xiàn)如果將Title組件獨(dú)立出去,會(huì)導(dǎo)致更多的麻煩,主要出現(xiàn)在數(shù)據(jù)傳輸上,即,數(shù)據(jù)需要從Server傳入Structure中,再傳入title中,因此,最終刪除了title組件。最終保留了三個(gè)組件:

在確定好這些之后,就可以開(kāi)始頁(yè)面的搭建了。
在每個(gè)組件中填入需要渲染的元素即可。本項(xiàng)目的主體是使用表格實(shí)現(xiàn)的。
2. UI庫(kù)使用
第一次開(kāi)發(fā),使用了公司一直在用的antd庫(kù),主要使用的組件有:Button, Table, Icon, Tooltip, Tag, Modal, Row, Col等。
在使用antd庫(kù)的時(shí)候,有很多注意事項(xiàng),這在我的另一篇文章中有提到,這里不做詳述了。
本項(xiàng)目的主體是使用antd中的Table組件搭起來(lái)的。該組件有兩個(gè)主要的屬性:columns&&dataSource。columns用來(lái)設(shè)置每一列的值,dataSource則用來(lái)選擇數(shù)據(jù)源。最終使用<Table />進(jìn)行生成。當(dāng)然,該組件也有很多其他屬性,本項(xiàng)目做的配置如下:

其他組件在另一篇文章中也有所提及,因此不做詳述;值得一提的是,在使用Modal組件時(shí),發(fā)現(xiàn)其中的文字無(wú)法選擇,后來(lái)發(fā)現(xiàn)是一個(gè)css3的屬性:user-select設(shè)置成了none, 因此,在antd.min.css文件中進(jìn)行了定制化主題,將所有的user-select屬性值都改為了text(使用正則表達(dá)式
(?<=user-select:)none 進(jìn)行向前匹配并進(jìn)行替換,效率更高哦)。附:user-select的屬性值:
auto——默認(rèn)值,用戶可以選中元素中的內(nèi)容
text——用戶可以選擇元素中的文本
element——文本可選,但僅限元素的邊界內(nèi)(只有IE和FF支持)
all——在編輯器內(nèi),如果雙擊或上下文點(diǎn)擊發(fā)生在子元素上,該值的最高級(jí)祖先元素將被選中。
在所有的組件插入后,最重要的數(shù)據(jù)傳輸步驟來(lái)到了!前方高能。。。
3. 交互實(shí)現(xiàn)
該項(xiàng)目是一個(gè)異步管理項(xiàng)目,主要需要進(jìn)行的數(shù)據(jù)交互是:點(diǎn)擊按鈕后發(fā)出不同的數(shù)據(jù)請(qǐng)求,從后端拉回新的返回?cái)?shù)據(jù),同時(shí)更新UI界面。
要實(shí)現(xiàn)這樣的異步傳輸系統(tǒng),分為幾個(gè)步驟:
1)點(diǎn)擊按鈕的時(shí)候觸發(fā)事件,同時(shí)傳遞參數(shù);
2)向指定接口傳遞數(shù)據(jù);
3)從指定接口獲取最新數(shù)據(jù),更新UI頁(yè)面;
這樣,就完成了一個(gè)數(shù)據(jù)傳輸?shù)倪^(guò)程。
再看具體實(shí)現(xiàn)。
首先是第一步,具體實(shí)現(xiàn)方法如下:
<button onClick={ () => this.props.startAll({
server_name:name})}
className='actionButton'
>...</button>
在一個(gè)button中綁定startAll方法,并傳入server_name參數(shù);
接下來(lái),是通過(guò)startAll方法向指定接口傳參,這里使用的是react-refetch。使用react-refetch,首先需要引入,這里主要是引入connect:
import { connect, PromiseState } from 'react-refetch'
接著就是在項(xiàng)目中觸發(fā),本項(xiàng)目中選擇的觸發(fā)方式:
export default connect (props=>({
startAll: params=>({
startAllResponse: {
url:/smonitor/api/***,
method:'POST',
body: formurlencoded(params),
force:true,
headers: {
'Content-Type':'application/x-www-form-urlencoded'
}
}
})
//通過(guò)這樣的操作,將請(qǐng)求參數(shù)發(fā)出;
最后,從指定接口獲取數(shù)據(jù)并進(jìn)行UI界面更新:
要完成此操作,首先應(yīng)該通過(guò)react的生命周期componentWillReceiveProps來(lái)完成,通過(guò)向此周期中傳入nextProps參數(shù),來(lái)檢測(cè)當(dāng)整個(gè)app的props改變之后,項(xiàng)目需要進(jìn)行的操作。在此項(xiàng)目中,這一步需要顯示從后端拉過(guò)來(lái)新的服務(wù)器狀態(tài),因此,需要仿照第二步的做法,新建一個(gè)refresh方法,并且重新向服務(wù)器請(qǐng)求參數(shù):
refreshList:params => ({
refreshAllResponse: {
url: `/smonitor/api/***`,
method: 'POST',
body: formurlencoded(params),
force: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
}
)
之后,在componentWillReceiveProps生命周期中引入這個(gè)方法,來(lái)刷新整個(gè)頁(yè)面的顯示:
componentWillReceiveProps(nextProps) {
if (nextProps.startAllResponse && !nextProps.startAllResponse.pending && this.props.startAllResponse.pending) {
const responseValue = nextProps.startAllResponse.value;
if (responseValue.errno == 0) {
this.props.refreshList({
server_name:this.props.name
})
}
}
通過(guò)這樣的方式,就完成了頁(yè)面的刷新顯示。
4. 狀態(tài)管理引入
這一步主要做的是引入redux管理機(jī)制。目前還沒(méi)做,只提出設(shè)想。
首先是將所有的動(dòng)作(包含點(diǎn)擊按鈕后的行為)集成到action中,之后在reducer中寫(xiě)入生命周期中的內(nèi)容,最后通過(guò)connect將recucer將module和action相連接。
具體的實(shí)現(xiàn)了再補(bǔ)充過(guò)來(lái)。。。
四、本地?cái)?shù)據(jù)測(cè)試
在整體的邏輯走通之后,接下來(lái)就是使用本地的mock數(shù)據(jù)進(jìn)行調(diào)試。具體的格式要與服務(wù)器請(qǐng)求的相同。
{
"homepage": {
"logout_url": "http:***,
"user_info": {
"uid": "344",
"uname": "xuran",
"email": "xuran@iwaimai.baidu.com"
},
"local-server": {
"version": "3.3.1",
"process_list": [
{
"group": "apilog01",
"name": "apilog001",
"description": "pid 2813, uptime 0:00:02",
"statename": "STOPPED",
"state": 0
}
]
},
"error-server": {
"version": "3.3.1",
"process_list": [
{
"group": "apilog02",
"name": "apilog001",
"description": "pid 2813, uptime 0:00:02",
"statename": "EXITED",
"state": 100
}
]
}
}
}
本地?cái)?shù)據(jù)測(cè)試無(wú)誤后,可以推代碼到后端開(kāi)發(fā)機(jī)上進(jìn)行測(cè)試。
五、坑。。。
- mock數(shù)據(jù)的配置問(wèn)題:之前啟動(dòng)項(xiàng)目時(shí),mock數(shù)據(jù)一直顯示404,經(jīng)查找之后發(fā)現(xiàn),是mock數(shù)據(jù)路徑?jīng)]有被攔截成功的原因,這在eden的項(xiàng)目配置過(guò)程中需要重點(diǎn)注意;
- 注意上線環(huán)境與本機(jī)環(huán)境的區(qū)別。兩個(gè)大坑:第一個(gè)是使用的iconfont字體的跨域問(wèn)題,阿里cdn會(huì)攔截本廠域名,因此通過(guò)下載到本地并改變webpack配置來(lái)解決這一點(diǎn);第二個(gè)是要將所有本地內(nèi)容轉(zhuǎn)換為線上內(nèi)容,比如登出鏈接,就需要從模板中取。。。
- 處理數(shù)據(jù)時(shí)要考慮到數(shù)據(jù)結(jié)構(gòu)可能進(jìn)行擴(kuò)充,因此需要進(jìn)行多種考慮。本項(xiàng)目中,
homepage下原先只有local-server這一個(gè)對(duì)象,后來(lái)又?jǐn)U展了logout_url和user_info這兩個(gè)字段,因此之前的數(shù)據(jù)讀取方式不能再用,這是一開(kāi)始開(kāi)發(fā)時(shí)沒(méi)有考慮到的情況。
這樣,算是跌跌撞撞完成了第一個(gè)項(xiàng)目的開(kāi)發(fā)和上線。日后的優(yōu)化方向:引入redux狀態(tài)管理機(jī)制&&完善Eden。
未完待續(xù)。。