原文鏈接: http://www.luckyjing.com/posts/front-end/arch-explore.html
回顧大學(xué)四年,在學(xué)院課程大作業(yè)范圍內(nèi)的項(xiàng)目沐兰,并沒(méi)有一次印象深刻的團(tuán)隊(duì)開(kāi)發(fā)經(jīng)歷哆档,往往都是三兩程序員一聚,便草草開(kāi)始了僧鲁。在阿里云實(shí)習(xí)的幾個(gè)月里,相比代碼本身象泵,學(xué)到的最多的便是人力資源的合理利用寞秃,工具的效率輔助,年已至末偶惠,最后一次大作業(yè)春寿,將自己在實(shí)習(xí)過(guò)程中積累的經(jīng)驗(yàn)運(yùn)用起來(lái),指定合理規(guī)范的項(xiàng)目開(kāi)發(fā)流程忽孽,平穩(wěn)地推進(jìn)項(xiàng)目的開(kāi)發(fā)绑改,不會(huì)讓項(xiàng)目邊界越來(lái)越大以至于收不住邊界,特地做此記錄分享給大家兄一。
背景
項(xiàng)目背景
本次的項(xiàng)目是一個(gè)在線購(gòu)物商城系統(tǒng)溺欧,這個(gè)詞相信大家已經(jīng)耳熟能詳了樱哼,具體細(xì)節(jié)不必多說(shuō),分為三個(gè)子系統(tǒng)Customer,Owner,Administrator
,需要將項(xiàng)目分為三個(gè)階段弃理,Release1,2,3
,采用原型開(kāi)發(fā)模式咪奖,每個(gè)版本給出一個(gè)可供交付的產(chǎn)品辅鲸,總結(jié)來(lái)說(shuō),項(xiàng)目性質(zhì)為無(wú)復(fù)雜功能點(diǎn)展箱,但業(yè)務(wù)量繁多旨枯。
成員介紹
團(tuán)隊(duì)成員共有20人,其中開(kāi)發(fā)同學(xué)共10位混驰,前端同學(xué)4位攀隔,后端同學(xué)6位。首先進(jìn)行技術(shù)調(diào)研栖榨,和在公司不同竞慢,在公司大家技術(shù)棧其實(shí)差不多,在學(xué)校則不是治泥,最終得出結(jié)果筹煮,前端熟悉React
同學(xué)1位,其余3位熟悉jQuery
居夹,后端同學(xué)全部熟悉SSH框架
败潦,為保證后端的統(tǒng)一本冲,后端技術(shù)棧不作調(diào)整,但前端因考慮到自動(dòng)化構(gòu)建的工具的接入劫扒, 以及用jQuery
前期成本小檬洞,后期成本大,難以維護(hù)的現(xiàn)狀沟饥,故全部采用React
技術(shù)棧添怔,并對(duì)不熟悉的同學(xué)進(jìn)行一次集體培訓(xùn)以及后續(xù)答疑解惑。
前期準(zhǔn)備
三個(gè)子系統(tǒng)之間其實(shí)前端關(guān)聯(lián)度不大贤旷,可以認(rèn)為是三個(gè)獨(dú)立的系統(tǒng)广料,對(duì)于前端而言,React全家桶
搭建起來(lái)并不容易幼驶,涉及到代碼目錄的規(guī)定艾杏,Webpack
的配置等等,故前端由我做出基層的腳手架盅藻,實(shí)現(xiàn)業(yè)務(wù)同學(xué)可以直接寫業(yè)務(wù)购桑,無(wú)需關(guān)系構(gòu)件流程的目的,對(duì)于后端而言氏淑,不同數(shù)據(jù)之間是共享的勃蜘,故由一名主力后端同學(xué)負(fù)責(zé)數(shù)據(jù)庫(kù)的設(shè)計(jì)與構(gòu)建,并且搭建好基礎(chǔ)后端目錄假残,其他同學(xué)在此基礎(chǔ)上進(jìn)行業(yè)務(wù)層面的開(kāi)發(fā)元旬。
項(xiàng)目構(gòu)建歷程
基礎(chǔ)結(jié)構(gòu)預(yù)覽
在看過(guò)上面這個(gè)圖以后,項(xiàng)目的整個(gè)輪廓已經(jīng)清晰守问,為了實(shí)現(xiàn)以上架構(gòu)匀归,我們需要定一些小小目標(biāo):
-
前后端采用分離模式開(kāi)發(fā),通過(guò)一份提前給出的
API
文檔進(jìn)行協(xié)商耗帕,數(shù)據(jù)則通過(guò)以json
作為基礎(chǔ)格式的接口進(jìn)行交互穆端。 -
打造一個(gè)
Mock
服務(wù)器,實(shí)現(xiàn)API文檔已給出
仿便,但后端接口還沒(méi)有寫好的時(shí)候体啰,前端通過(guò)模擬接口可以透明地請(qǐng)求接口了。(注:透明的意思即Mock服務(wù)器可以攔截前端的所有請(qǐng)求嗽仪,達(dá)到和真實(shí)線上服務(wù)調(diào)用接口一樣的效果) -
動(dòng)靜資源分離荒勇,我們只有一臺(tái)服務(wù)器,是很實(shí)惠阿里云學(xué)生機(jī)闻坚,所以前后端數(shù)據(jù)都要放在這臺(tái)機(jī)器上沽翔,前端靜態(tài)資源存儲(chǔ)在
nginx
服務(wù)器下,后端服務(wù)放在tomcat/webapps
目錄下,因?yàn)闉閱雾?yè)應(yīng)用仅偎,后端只需要在那份index.html
里引入前端靜態(tài)資源就可以了(直接寫入絕對(duì)路徑)跨蟹,最終訪問(wèn)的時(shí)候,服務(wù)器能夠辨別是請(qǐng)求靜態(tài)資源還是后端服務(wù)器橘沥,也就是俗稱的動(dòng)靜分離
窗轩。 - 快速部署,部署的時(shí)候如何輕松,快捷座咆,避免配置各種環(huán)境痢艺,要配也是一個(gè)人配,不能讓開(kāi)發(fā)同學(xué)苦惱介陶。
下面我們來(lái)一步步實(shí)現(xiàn)堤舒。
前端
簡(jiǎn)述
- 前后端分離開(kāi)發(fā),所以路由要放到前端斤蔓,用
React-Router
植酥。 - 前端要自動(dòng)化構(gòu)建镀岛,用
Webpack
弦牡。 -
React+Redux
配置其實(shí)挺麻煩的,如果能寫好模板漂羊,然后自動(dòng)生成就好了驾锰,用redux-cli
。 -
mock2easy
作為mock服務(wù)器走越。
基礎(chǔ)目錄結(jié)構(gòu)
在項(xiàng)目開(kāi)發(fā)前椭豫,我提前將項(xiàng)目的腳手架提前搭建好,項(xiàng)目的大概目錄如下所示:
├── README.md
├── blueprints # 這就是上述第三點(diǎn)的模板旨指,大家可以去github上搜 redux-cli
├── build # 構(gòu)建后的文件
├── dev.js # 類似webpack-dev-server 加入了mock的服務(wù)
├── package.json
├── src
└── webpack.config.js
自動(dòng)化構(gòu)建命令
使用Webpack
提前寫好配置文件赏酥,不管webpack.config.js
里有多少步驟,最終呈現(xiàn)出來(lái)的只有build
目錄下那幾個(gè)打包好的文件谆构,將相關(guān)命令寫到package.json
中裸扶,前端同學(xué)只需執(zhí)行一個(gè)命令就可以啟動(dòng)服務(wù)了。
"scripts": {
"dev": "node dev.js",
"pub": "NODE_ENV=production webpack && git add . && git commit -m':grin:發(fā)布新功能' && git push origin master"
}
mock服務(wù)
mock服務(wù)器什么原理搬素,一張圖給大家展示一下呵晨。
解釋一下,webpack-dev-server
簡(jiǎn)單點(diǎn)就是一個(gè)express
的服務(wù)器熬尺,那么如果我們直接用它的話摸屠,直接訪問(wèn)/ajax/test.json
肯定是跑不起來(lái)的,因?yàn)檫@個(gè)接口404啊粱哼,所以我們要想實(shí)現(xiàn)如下目標(biāo):既能擁有webpack-dev-server
獨(dú)特的熱刷新功能季二,還可以當(dāng)請(qǐng)求后綴為xxx.json
的時(shí)候,去請(qǐng)求一個(gè)mock
服務(wù)器去拿數(shù)據(jù)然后返回揭措,我們這個(gè)mock
服務(wù)使用的是mock2easy
戒傻,大家可以去GitHub
上搜搜看税手,一切皆透明,于是我們的dev.js
配置如下所示:
// 先實(shí)現(xiàn)一個(gè)webpack-dev-server
var app = connect();
var compiler = webpack(config);
app.use(require("webpack-dev-middleware")(compiler, config.devServer));
app.use(require("webpack-hot-middleware")(compiler));
// 核心代碼 之 mock服務(wù)就是一個(gè)中間件
app.use(function(req,res,next){
if (req.url.indexOf('.json') == -1) {
return next();
}
// 發(fā)現(xiàn)是接口請(qǐng)求需纳,然后去請(qǐng)求mock服務(wù)器芦倒,然后把數(shù)據(jù)返回
var _req = http.request(options, function (_res) {
var data = '';
_res.on('data', function (chunk) {
data += chunk;
});
_res.on('end', function () {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(data);
});
});
});
app.listen(config.devServer.port, function () {
console.log('開(kāi)發(fā)環(huán)境已經(jīng)啟動(dòng): http://127.0.0.1:' + port);
});
// 啟動(dòng)mock服務(wù)器
require('mock2easynew')(options, function (app) {
app.listen(options.port, function () {
console.log('mock服務(wù)器已經(jīng)啟動(dòng),地址:http://localhost:' + options.port);
});
});
優(yōu)化
- 在開(kāi)發(fā)時(shí)不翩,
React
,Redux
就不用被抽離出來(lái)了兵扬,當(dāng)然代碼也不用壓縮了,一切以快為主口蝠。 - 在發(fā)布時(shí)器钟,將
React
,Redux
這些庫(kù)抽離出來(lái),這樣打包出來(lái)文件會(huì)小妙蔗,庫(kù)文件我們可以用免費(fèi)的CDN
進(jìn)行引入傲霸。
<script src="http://cdn.bootcss.com/react/15.4.0/react.min.js"></script>
<script src="http://cdn.bootcss.com/react/15.4.0/react-dom.min.js"></script>
<script src="http://cdn.bootcss.com/redux/3.6.0/redux.min.js"></script>
實(shí)現(xiàn)以上優(yōu)化,我們需要在我們的webpack.config.js
中加入如下邏輯眉反,即需要擁有區(qū)分開(kāi)發(fā)環(huán)境的能力昙啄。
// 區(qū)別環(huán)境
var debug = process.env.NODE_ENV !== 'production';
// 第三方資源
var externals = {
"react": 'React',
"react-dom": "ReactDOM",
'redux': 'Redux'
};
var config = {
externals: debug ? {} : externals,
}
經(jīng)過(guò)以上步驟之后,我們的前端開(kāi)發(fā)環(huán)境已經(jīng)搭建好了寸五,開(kāi)發(fā)同學(xué)只要先去填寫一些mock
的API梳凛,然后就可以在前端直接訪問(wèn)到了,所以剩下的只需要和后端進(jìn)行約定下接口名稱就可以了梳杏。
# step1
前后端約定API名稱為:/ajax/profile.json
# step2
前端(假設(shè)是3005端口)在mock服務(wù)器(假設(shè)是8005端口)添加這個(gè)接口 && 后端正常工作...
# step3
訪問(wèn)localhost:3005/ajax/profile.json 成功拿到數(shù)據(jù)
# step4
前端代碼里直接書寫 $.ajax('/ajax/profile.json') 即可
后端簡(jiǎn)介
- 后端基本上盡可能也要自動(dòng)化韧拒,快捷化,所以使用
maven
去進(jìn)行項(xiàng)目的構(gòu)建十性,達(dá)到擁有一份源代碼便可四處運(yùn)行的目的叛溢。 - 使用
postman
讓后端同學(xué)進(jìn)行接口的測(cè)試。
服務(wù)器部署實(shí)戰(zhàn)
到目前為止劲适,前后端同學(xué)都可以在各自本地進(jìn)行業(yè)務(wù)開(kāi)發(fā)了楷掉,而且相互不耦合,以一份API文檔為準(zhǔn)便可以開(kāi)發(fā)减响,接下來(lái)要討論的就是構(gòu)建的事情了靖诗,我們要達(dá)到的目的是:前端打包好后的靜態(tài)資源可以自動(dòng)部署到服務(wù)器上,后端代碼推到倉(cāng)庫(kù)后支示,會(huì)自動(dòng)進(jìn)行maven構(gòu)建刊橘,打war包,并自動(dòng)上傳到服務(wù)器上颂鸿,重啟tomcat服務(wù)器促绵。
為了實(shí)現(xiàn)以上目的,項(xiàng)目采用阿里云code
進(jìn)行代碼管理,與此同時(shí)败晴,使用阿里云CRP
服務(wù)實(shí)現(xiàn)上述持續(xù)化浓冒,描述如下:
- CRP服務(wù)檢測(cè)指定的遠(yuǎn)程倉(cāng)庫(kù)
master
分支是否有更新 - 如果有更新,則執(zhí)行一段腳本(由用戶輸入尖坤,往往為一些構(gòu)建命令)
- 用戶指定需要上傳的文件(往往是第二步驟的產(chǎn)出)
- 用戶指定需要上傳到的服務(wù)器以及路徑
- 用戶指定上傳結(jié)束后需要執(zhí)行的一段腳本(往往為一些部署前的命令稳懒,比如移動(dòng)靜態(tài)資源到指定目錄,或關(guān)閉tomcat慢味,解壓縮包场梆,重啟tomcat)
還需要對(duì)nginx
做一下簡(jiǎn)單配置,實(shí)現(xiàn)動(dòng)靜分離纯路。
# 由nginx處理靜態(tài)頁(yè)面
location ~ .*\.(gif|jpg|png|bmp|swf)$
{
expires 30d; #使用expires緩存模塊或油,緩存到客戶端30天
}
location ~ .*\.(html|jsp|js|css)?$
{
expires 1d;
}
# 其余請(qǐng)求全部轉(zhuǎn)到tomcat
location ~ .*$ {
proxy_pass http://127.0.0.1:8080;
}
我們集成后的效果如下,當(dāng)集成完畢后驰唬,可以通知測(cè)試同學(xué)進(jìn)行及時(shí)測(cè)試顶岸,提前暴露出問(wèn)題,保證項(xiàng)目的穩(wěn)步前進(jìn)叫编。
總結(jié)
全程構(gòu)建系統(tǒng)的過(guò)程中辖佣,一直遵循的守則是自動(dòng)化越高越好(redux-cli
,maven
,持續(xù)交付
),細(xì)節(jié)屏蔽的越高越好(執(zhí)行命令寫到package.json
里)宵溅,將部署步驟和開(kāi)發(fā)步驟相互隔離凌简,開(kāi)發(fā)同學(xué)只需要專注業(yè)務(wù)的開(kāi)發(fā)即可上炎,這樣便可以三個(gè)子系統(tǒng)同時(shí)前進(jìn)恃逻,開(kāi)發(fā)同學(xué)可以有更多的時(shí)間去考慮代碼本身的質(zhì)量。
能夠讓我們放心去構(gòu)建整個(gè)系統(tǒng)的基礎(chǔ)藕施,屏蔽底層的不同寇损,多虧了在背后運(yùn)行的Docker
服務(wù),本次項(xiàng)目中的嘗鮮體驗(yàn)已經(jīng)感受到了好處裳食,將Docker
納入項(xiàng)目的部署環(huán)境矛市,往往會(huì)帶來(lái)驚喜。
路還長(zhǎng)诲祸,保持新鮮感浊吏,不斷去構(gòu)造更加完善的自動(dòng)化系統(tǒng)。