- 什么是前后端分離?
- 為什么前后端分離?
- 前后端分離的優(yōu)勢(shì)?
未分離時(shí)期
MVC: 早期JSP+SERVLET中的結(jié)構(gòu)圖如下
大致就是所有的請(qǐng)求都被發(fā)送給作為控制器的Servlet碴巾,它接受請(qǐng)求室叉,并根據(jù)請(qǐng)求信息將它們分發(fā)給適當(dāng)?shù)腏SP來(lái)響應(yīng)栋荸。同時(shí)着绊,Servlet還根據(jù)JSP的需求生成JavaBeans的實(shí)例并輸出給JSP環(huán)境离斩。JSP可以通過(guò)直接調(diào)用方法或使用UseBean的自定義標(biāo)簽得到JAVABeans中的數(shù)據(jù)腮鞍。需要說(shuō)明的是睦疫,這個(gè)View還可以采用 Velocity、Freemaker 等模板引擎因苹。使用了這些模板引擎苟耻,可以使得開(kāi)發(fā)過(guò)程中的人員分工更加明確,還能提高開(kāi)發(fā)效率扶檐。
那么凶杖,在這個(gè)時(shí)期,開(kāi)發(fā)方式有如下兩種:
方法一:
方法二:
方式一已經(jīng)逐漸淘汰款筑。主要原因有兩點(diǎn)
(1)前端在開(kāi)發(fā)過(guò)程中嚴(yán)重依賴后端智蝠,在后端沒(méi)有完成的情況下,前端根本無(wú)法干活
(2)由于趨勢(shì)問(wèn)題奈梳,會(huì)JSP,懂velocity,freemarker的前端越來(lái)越少杈湾。
因此,方式一逐漸不被采用攘须。然而漆撞,不得不說(shuō)一點(diǎn),方式二其實(shí)很多小型傳統(tǒng)軟件公司至今還在使用于宙。
那么浮驳,方式一和方式二具有哪些共同的缺點(diǎn)呢?
1)、前端無(wú)法單獨(dú)調(diào)試
在項(xiàng)目上線后捞魁,遇到一些問(wèn)題至会。比如樣式出問(wèn)題,由于前端不具備項(xiàng)目開(kāi)發(fā)環(huán)境谱俭,那么就有可能出現(xiàn)如下對(duì)話
后端:"這個(gè)頁(yè)面還有有點(diǎn)錯(cuò)誤"
前端:"我這里沒(méi)問(wèn)題啊奋献。你那里不正常么?"
后端:"我這里不正常啊旺上。要不你過(guò)來(lái)看一下吧?"
前端:"我這沒(méi)環(huán)境,一時(shí)我也看不出問(wèn)題糖埋,怎么辦宣吱?"
后端:"你沒(méi)環(huán)境,坐我這邊調(diào)吧瞳别。"
然后征候,前端就滿臉不爽的在后端那調(diào)代碼了杭攻。
更有些情商低的后端就直接在旁邊開(kāi)按手機(jī),實(shí)在是疤坝。兆解。。跑揉。锅睛。
總結(jié):因?yàn)榍岸藷o(wú)法單獨(dú)調(diào)試,一方面開(kāi)發(fā)效率降低历谍,另一方面现拒,還有可能引發(fā)公司內(nèi)部人員上的矛盾。
2)望侈、前端不可避免會(huì)遇到后臺(tái)代碼
比如前端可能碰到如下結(jié)構(gòu)的代碼
<body>
<%
request.setCharacterEncoding("utf-8")
String name=request.getParameter("username");
out.print(name);
%>
</body>
前端在頁(yè)面里看到了后臺(tái)代碼印蔬,必然內(nèi)心是十分不快的分冈,這種方式耦合性太強(qiáng)戚嗅。
那么,就算你用了freemarker等模板引擎撩幽,不用寫JAVA代碼,那前端也不可避免的要去重新學(xué)習(xí)該模板引擎的模板語(yǔ)法捐韩,無(wú)謂增加了前端的學(xué)習(xí)成本退唠。
正如后端開(kāi)發(fā)不想寫前端一樣,你想想如果你的后臺(tái)代碼里嵌入前端代碼奥帘,你是什么感受铜邮?
因此,這種方式十分不妥寨蹋。
3)松蒜、JSP本身所導(dǎo)致的一些其他問(wèn)題
JSP的痛點(diǎn)
1、動(dòng)態(tài)資源和靜態(tài)資源全部耦合在一起已旧,服務(wù)器壓力大秸苗,因?yàn)榉?wù)器會(huì)收到各種http請(qǐng)求,例如css的http請(qǐng)求运褪,js的惊楼,圖片的等等。一旦服務(wù)器出現(xiàn)狀況秸讹,前后臺(tái)一起玩完檀咙,用戶體驗(yàn)極差。
2璃诀、UI出好設(shè)計(jì)圖后弧可,前端工程師只負(fù)責(zé)將設(shè)計(jì)圖切成html,需要由java工程師來(lái)將html套成jsp頁(yè)面劣欢,出錯(cuò)率較高棕诵,修改問(wèn)題時(shí)需要雙方協(xié)同開(kāi)發(fā)裁良,效率低下。
3校套、jsp必須要在支持java的web服務(wù)器里運(yùn)行(例如tomcat价脾,jetty,resin等)笛匙,無(wú)法使用nginx等侨把,性能提不上來(lái)。(nginx據(jù)說(shuō)單實(shí)例http并發(fā)高達(dá)5w膳算,這個(gè)優(yōu)勢(shì)要用上)
4座硕、第一次請(qǐng)求jsp較慢,第一次運(yùn)行必須要在web服務(wù)器中編譯成servlet涕蜂,速度會(huì)較慢华匾。
5、每次請(qǐng)求jsp都是訪問(wèn)servlet再用輸出流輸出的html頁(yè)面机隙,效率沒(méi)有直接使用html高(是每次喲蜘拉,親~)。
6有鹿、jsp內(nèi)有較多標(biāo)簽和表達(dá)式旭旭,前端工程師在修改頁(yè)面時(shí)會(huì)捉襟見(jiàn)肘,遇到很多痛點(diǎn)葱跋。
7持寄、如果jsp中的內(nèi)容很多,頁(yè)面響應(yīng)會(huì)很慢娱俺,因?yàn)槭峭郊虞d稍味。
8、需要前端工程師使用java的ide(例如eclipse,用eclipse開(kāi)發(fā)前端代碼真的不好用...)荠卷,以及需要配置各種后端的開(kāi)發(fā)環(huán)境模庐,你們有考慮過(guò)前端工程師的感受嗎。
基于上述的一些痛點(diǎn)油宜,我們應(yīng)該把整個(gè)項(xiàng)目的開(kāi)發(fā)權(quán)重往前移掂碱,實(shí)現(xiàn)前后端真正的解耦!
半分離時(shí)期
前后端半分離慎冤,前端負(fù)責(zé)開(kāi)發(fā)頁(yè)面疼燥,通過(guò)Ajax調(diào)用后臺(tái)接口獲取數(shù)據(jù),然后采用dom操作對(duì)頁(yè)面進(jìn)行數(shù)據(jù)綁定蚁堤,最終是由前端把頁(yè)面渲染出來(lái)醉者。這就是Ajax與SPA應(yīng)用(單頁(yè)應(yīng)用)結(jié)合的方式。其結(jié)構(gòu)圖如下
步驟如下:
- 1)瀏覽器請(qǐng)求,cdn返回html頁(yè)面
- 2)html中的js代碼以ajax方式請(qǐng)求后臺(tái)的restful接口
- 3)接口返回json數(shù)據(jù)湃交,頁(yè)面解析json數(shù)據(jù),通過(guò)dom操作渲染頁(yè)面
ps:博主早期就是用jquery的ajax請(qǐng)求藤巢,然后這么做的搞莺。為什么說(shuō)是半分離的?因?yàn)椴皇撬许?yè)面都是單頁(yè)面應(yīng)用掂咒,在多頁(yè)面應(yīng)用的情況下才沧,前端因?yàn)闆](méi)有掌握controller層,前端需要跟后端討論绍刮,我們這個(gè)頁(yè)面是要同步輸出呢温圆,還是異步j(luò)son渲染呢?因此孩革,在這一階段岁歉,只能算半分離。這種方式的優(yōu)缺點(diǎn)有哪些呢膝蜈?
首先锅移,這種方式的優(yōu)點(diǎn)是很明顯的。
前端不會(huì)嵌入任何后臺(tái)代碼饱搏,前端專注于html非剃、css、js的開(kāi)發(fā)推沸,不依賴于后端备绽。
自己還能夠模擬json數(shù)據(jù)來(lái)渲染頁(yè)面。
發(fā)現(xiàn)bug鬓催,也能迅速定位出是誰(shuí)的問(wèn)題肺素,不會(huì)出現(xiàn)互相推脫的現(xiàn)象。
然而深浮,在這種架構(gòu)下压怠,還是存在明顯的弊端的。
最明顯的有如下幾點(diǎn):
(1)js存在大量冗余飞苇,在業(yè)務(wù)復(fù)雜的情況下菌瘫,頁(yè)面的渲染部分的代碼,非常復(fù)雜布卡。
(2)在json返回的數(shù)據(jù)比較大的情況下雨让,渲染的十分緩慢,會(huì)出現(xiàn)頁(yè)面卡頓的情況
(3)seo非常不方便忿等,由于搜索引擎的爬蟲無(wú)法爬下js異步渲染的數(shù)據(jù)栖忠,導(dǎo)致這樣的頁(yè)面,SEO會(huì)存在一定的問(wèn)題。
(4)資源消耗嚴(yán)重庵寞,在業(yè)務(wù)復(fù)雜的情況下狸相,一個(gè)頁(yè)面可能要發(fā)起多次http請(qǐng)求才能將頁(yè)面渲染完畢【璐ǎ可能有人不服脓鹃,覺(jué)得pc端建立多次http請(qǐng)求也沒(méi)啥。那你考慮過(guò)移動(dòng)端么古沥,知道移動(dòng)端建立一次http請(qǐng)求需要消耗多少資源么瘸右?
正是因?yàn)槿缟先秉c(diǎn),真正的前后端分離架構(gòu)誕生了
分離時(shí)期
在這一時(shí)期岩齿,擴(kuò)展了前端的范圍太颤。認(rèn)為controller層也屬于前端的一部分。在這一時(shí)期前端:負(fù)責(zé)View和Controller層盹沈。后端:只負(fù)責(zé)Model層龄章,業(yè)務(wù)處理/數(shù)據(jù)等〗笾睿可是前端不懂后臺(tái)代碼呀瓦堵?controller層如何實(shí)現(xiàn)呢?這就是node.js的妙用了歌亲,node.js適合運(yùn)用在高并發(fā)菇用、I/O密集、少量業(yè)務(wù)邏輯的場(chǎng)景陷揪。最重要的一點(diǎn)是惋鸥,前端不用再學(xué)一門其他的語(yǔ)言了,對(duì)前端來(lái)說(shuō)悍缠,上手度大大提高卦绣。于是,這一時(shí)期架構(gòu)圖如下
<!--服務(wù)器端渲染 -->
<select>
<option value=''>--請(qǐng)選擇所屬業(yè)務(wù)--</option>
{% for p in p_list %}
<option value="{{ p }}">{{ p }}</option>
{% endfor %}
</select>
這是前后端耦合的飞蚓,可讀性差
<!--前端渲染 -->
<template>
<select id="rander">
<option value=''>--請(qǐng)選擇所屬業(yè)務(wù)--</option>
<option v-for="list in lists" :value="list" v-text="list"></option>
</select>
</template>
<script>
export default {
data: {
return {
lists: ['選項(xiàng)一', '選項(xiàng)二', '選項(xiàng)三', '選項(xiàng)四']
}
}滤港,
ready: function () {
this.$http({
url: '/demo/',
method: 'POST',
})
.then(function (response) {
this.lists = response.data.lists // 獲取服務(wù)器端數(shù)據(jù)并渲染
})
}
}
</script>
上面是前端渲染的一段代碼,前端通過(guò)AJAX調(diào)用后臺(tái)接口趴拧,數(shù)據(jù)邏輯放在前端溅漾,由前端維護(hù)。
前后分離的優(yōu)勢(shì)
- 1著榴、可以實(shí)現(xiàn)真正的前后端解耦添履,前端服務(wù)器使用nginx。前端/WEB服務(wù)器放的是css脑又,js暮胧,圖片等等一系列靜態(tài)資源(甚至你還可以css锐借,js,圖片等資源放到特定的文件服務(wù)器往衷,例如阿里云的oss钞翔,并使用cdn加速),前端服務(wù)器負(fù)責(zé)控制頁(yè)面引用&跳轉(zhuǎn)&路由席舍,前端頁(yè)面異步調(diào)用后端的接口嗅战,后端/應(yīng)用服務(wù)器使用tomcat(把tomcat想象成一個(gè)數(shù)據(jù)提供者),加快整體響應(yīng)速度俺亮。(這里需要使用一些前端工程化的框架比如nodejs,react疟呐,router脚曾,react,redux启具,webpack)
- 2本讥、發(fā)現(xiàn)bug,可以快速定位是誰(shuí)的問(wèn)題鲁冯,不會(huì)出現(xiàn)互相踢皮球的現(xiàn)象拷沸。頁(yè)面邏輯,跳轉(zhuǎn)錯(cuò)誤薯演,瀏覽器兼容性問(wèn)題撞芍,腳本錯(cuò)誤,頁(yè)面樣式等問(wèn)題跨扮,全部由前端工程師來(lái)負(fù)責(zé)序无。接口數(shù)據(jù)出錯(cuò),數(shù)據(jù)沒(méi)有提交成功衡创,應(yīng)答超時(shí)等問(wèn)題帝嗡,全部由后端工程師來(lái)解決。雙方互不干擾璃氢,前端與后端是相親相愛(ài)的一家人哟玷。
- 3、在大并發(fā)情況下一也,我可以同時(shí)水平擴(kuò)展前后端服務(wù)器巢寡,比如淘寶的一個(gè)首頁(yè)就需要2000+臺(tái)前端服務(wù)器做集群來(lái)抗住日均多少億+的日均pv。(去參加阿里的技術(shù)峰會(huì)塘秦,聽(tīng)他們說(shuō)他們的web容器都是自己寫的讼渊,就算他單實(shí)例抗10萬(wàn)http并發(fā),2000臺(tái)是2億http并發(fā)尊剔,并且他們還可以根據(jù)預(yù)知洪峰來(lái)無(wú)限拓展爪幻,很恐怖菱皆,就一個(gè)首頁(yè)。挨稿。仇轻。)
- 4、減少后端服務(wù)器的并發(fā)/負(fù)載壓力奶甘。除了接口以外的其他所有http請(qǐng)求全部轉(zhuǎn)移到前端nginx上篷店,接口的請(qǐng)求調(diào)用tomcat,參考nginx反向代理tomcat臭家。且除了第一次頁(yè)面請(qǐng)求外疲陕,瀏覽器會(huì)大量調(diào)用本地緩存。
- 5钉赁、即使后端服務(wù)暫時(shí)超時(shí)或者宕機(jī)了蹄殃,前端頁(yè)面也會(huì)正常訪問(wèn),只不過(guò)數(shù)據(jù)刷不出來(lái)而已你踩。
- 6诅岩、也許你也需要有微信相關(guān)的輕應(yīng)用,那樣你的接口完全可以共用带膜,如果也有app相關(guān)的服務(wù)吩谦,那么只要通過(guò)一些代碼重構(gòu),也可以大量復(fù)用接口膝藕,提升效率式廷。(多端應(yīng)用)
- 7、頁(yè)面顯示的東西再多也不怕芭挽,因?yàn)槭钱惒郊虞d懒棉。
- 8、nginx支持頁(yè)面熱部署览绿,不用重啟服務(wù)器策严,前端升級(jí)更無(wú)縫。
- 9饿敲、增加代碼的維護(hù)性&易讀性(前后端耦在一起的代碼讀起來(lái)相當(dāng)費(fèi)勁)妻导。
- 10、提升開(kāi)發(fā)效率怀各,因?yàn)榭梢郧昂蠖瞬⑿虚_(kāi)發(fā)倔韭,而不是像以前的強(qiáng)依賴。
- 11瓢对、在nginx中部署證書寿酌,外網(wǎng)使用https訪問(wèn),并且只開(kāi)放443和80端口硕蛹,其他端口一律關(guān)閉(防止黑客端口掃描)醇疼,內(nèi)網(wǎng)使用http硕并,性能和安全都有保障。
- 12秧荆、前端大量的組件代碼得以復(fù)用倔毙,組件化,提升開(kāi)發(fā)效率乙濒,抽出來(lái)陕赃!
分離所帶來(lái)的問(wèn)題
- (1)人員問(wèn)題
加重了前端團(tuán)隊(duì)的工作量,帶來(lái)了前端人員問(wèn)題,減輕了后端團(tuán)隊(duì)的工作量颁股,提高了性能和可擴(kuò)展性么库。 - (2)技術(shù)問(wèn)題
前后端分離的實(shí)現(xiàn)對(duì)技術(shù)人員尤其是前端人員的要求會(huì)上升一個(gè)層次,前端的工作不只是切頁(yè)面寫模板或是處理一些簡(jiǎn)單的js邏輯甘有,前端需要處理服務(wù)器返回的各種數(shù)據(jù)格式廊散,還需要掌握一系列的數(shù)據(jù)處理邏輯、MVC思想和各種主流框架梧疲。 - (3) 產(chǎn)品迭代周期問(wèn)題
采用分離架構(gòu),增加了一個(gè)接口制定流程和前后端聯(lián)調(diào)流程运准。從本質(zhì)上來(lái)說(shuō)幌氮,放慢了迭代周期。 - (4) 前端人員需要學(xué)習(xí)業(yè)務(wù)
本來(lái)前端只需要掌管視覺(jué)交互的部分⌒舶模現(xiàn)在因?yàn)閏ontroller層也歸前端管了该互,前端必須對(duì)公司的業(yè)務(wù)流程有深入的了解,才能準(zhǔn)確的寫出顯示邏輯韭畸。