前后分離架構(gòu)的探索之路
大約五年前,那時(shí)候我還是一個(gè)小小講師(蘋(píng)果 AATC 培訓(xùn)認(rèn)證),完全不懂編程為何物的菜鳥(niǎo),一個(gè)偶然的機(jī)會(huì)讓我進(jìn)入了公司的開(kāi)發(fā)部門(mén),任職什么呢?用戶體驗(yàn)設(shè)計(jì)師,原因很操蛋——我以前干過(guò)廣告設(shè)計(jì),做過(guò)餐飲服務(wù)行業(yè),因而我有兩個(gè)優(yōu)勢(shì):能聆聽(tīng)和揣摩客戶的需求,然后能做一些圖。
那時(shí)候很多像我們公司一樣的中小 IT 企業(yè)(200人左右,組成成分主要是大大小小的項(xiàng)目團(tuán)隊(duì))都有要做自主產(chǎn)品的訴求,這是市場(chǎng)決定的:出門(mén)找生意越來(lái)越難了。于是很多野路子出家的產(chǎn)品研發(fā)團(tuán)隊(duì)就這樣誕生了……
說(shuō)是產(chǎn)品研發(fā)團(tuán)隊(duì),其實(shí)都只是一群習(xí)慣了聽(tīng)命于人去按照 RFP 實(shí)現(xiàn)功能的碼農(nóng)罷了,和其他項(xiàng)目組相比唯一的差別大概就是“尚有夢(mèng)想的咸魚(yú)”而已。所以研發(fā)過(guò)程中的種種幼稚和操蛋,你用腳趾頭都能猜想得到。
一開(kāi)始我就是把各位老大的設(shè)想整理成人人看得懂的需求,然后把它們串起來(lái)畫(huà)成草圖(mockup)再交給各種工程師去實(shí)現(xiàn)好了,這個(gè)角色類似于如今很時(shí)髦的“產(chǎn)品經(jīng)理”。然而很快我發(fā)現(xiàn)大家老是加班,為什么呢?調(diào) CSS 樣式!
做慣了平面設(shè)計(jì)的我并不懂得把畫(huà)出來(lái)的東西變成瀏覽器里的東西會(huì)有多麻煩。(今天,我在面試一些切圖頁(yè)面仔時(shí),聽(tīng)他們大談特談像素級(jí)還原尚覺(jué)得好笑,但想想五年前的自己還是很有些羞愧的……然而更令我無(wú)語(yǔ)的是:到了如今初出茅廬的小前端們還把像素級(jí)還原的切頁(yè)面當(dāng)成是至高無(wú)上的本事,這件事情本身是不是很令人“沮喪”呢?)當(dāng)寫(xiě)頁(yè)面的同事一再告訴我我畫(huà)的東西不實(shí)際之后,我憋不住了——我就不信我畫(huà)的東西實(shí)現(xiàn)不出來(lái)!
抱著一口“怨氣”,我義無(wú)反顧的踏上了 HTML+CSS 這條路,其中過(guò)程不用多講,唯一的金玉良言只有一條:別看國(guó)內(nèi)的教程,別信 w3school 之類的拼湊資源站??傊@事兒的結(jié)果是半年以后整個(gè)項(xiàng)目組幾乎所有的頁(yè)面都是我來(lái)寫(xiě)了。(今天,已然成為前端架構(gòu)師的我,所有頁(yè)面的自定義樣式還是得我親自寫(xiě),我不怪任何人因?yàn)槲抑涝诤芏喙こ處焹?nèi)心里還是瞧不上寫(xiě) HTML+CSS 的技術(shù)的,你們不愿意學(xué)我不勉強(qiáng),我來(lái)。我還要感謝你們,為了能把飯喂到諸位的嘴里,我花費(fèi)大量的時(shí)間學(xué)習(xí) CSS 框架的開(kāi)發(fā),從而精通了整個(gè)生態(tài)鏈,從 pre-processing 一直到 post-processing)。
這個(gè)世界就是這樣:一旦你專精了一項(xiàng)技能,你會(huì)很容易看出相關(guān)的技能在目前的水準(zhǔn)如何。古人說(shuō):水漲船高。誠(chéng)不欺我也。
所以成天耳濡目染 HTML+CSS 的我,經(jīng)受著各種國(guó)外大神的視頻+教程耳提面命的我,很快就明白了一件事:我做的這個(gè)叫前端開(kāi)發(fā),HTML+CSS 只是起了一個(gè)頭,后面還有一座叫 JavaScript 的大山等著我。而我們做的前端開(kāi)發(fā)還很嫩,活該你成天加班改樣式,修 bug,因?yàn)槟阋婚_(kāi)始就沒(méi)走對(duì)路數(shù)。
幸好還不晚,不過(guò)這篇的主題是前后分離,所以我得按下快進(jìn)按鈕直奔主題而去。
JavaScript 對(duì)我來(lái)講太難太難了,但是 jQuery 尚可,因?yàn)樗蟹浅0舻?API 設(shè)計(jì)和兼容性處理,很適合我這樣的菜鳥(niǎo)入門(mén)。那時(shí)候有一個(gè)叫 Jeffery Way 的家伙錄制了一套 30 天學(xué)會(huì) jQuery 的教程讓我受益匪淺,我認(rèn)為他講得好,主要是兩個(gè)原因:
他不主要講各種 API 如何用,他從一開(kāi)始就給我貫徹了一個(gè)重要的思想:學(xué)會(huì)看文檔;
他主要講如何分析一個(gè)功能的實(shí)現(xiàn),如何組織以 jQuery 為核心的代碼邏輯;
在這里先插一段旁述。各位能在工作中使用 Rails 的同行們,你們是無(wú)比幸運(yùn)的!因?yàn)?Rails 已經(jīng)把 View-Template 這一環(huán)節(jié)梳理的足夠簡(jiǎn)單,哪怕完全不懂 Rails 的頁(yè)面仔,你稍微提點(diǎn)提點(diǎn),他也能很快學(xué)會(huì)如何把靜態(tài)頁(yè)面套進(jìn) Rails 的模版里去。
而我則是很不幸的,在那時(shí)我碰上了非常討厭的 JSP!你們千萬(wàn)別笑,隨便去問(wèn)那些寫(xiě)頁(yè)面出身的前端們對(duì) JSP 是什么感受,絕對(duì)不會(huì)有好臉色的。對(duì),我承認(rèn)自己很菜。寫(xiě)靜態(tài)頁(yè)面我行,但轉(zhuǎn)成 JSP 模版這件事在那時(shí)真的能把我難死!更要命的是,如果你把寫(xiě)好的頁(yè)面交給后端工程師去套模版,最終的結(jié)果就是一塌糊涂!沒(méi)錯(cuò),他們根本不會(huì)細(xì)心周到的照顧你精心設(shè)計(jì)的每一個(gè)標(biāo)簽,他們會(huì)做出各種各樣奇葩的事情來(lái)破壞原本完美的頁(yè)面結(jié)構(gòu),逼迫你不停的修改樣式和腳本來(lái)適應(yīng)這些“補(bǔ)丁”。
更要命的是調(diào)試!原本寫(xiě) HTML+CSS 一個(gè)輕量級(jí)編輯器就搞定了,但等他們轉(zhuǎn)成 JSP 之后你再想去調(diào)試就沒(méi)那么簡(jiǎn)單了。你需要:
運(yùn)行環(huán)境,比如 Java+Tomcat,不懂吧?沒(méi)事,學(xué)!
生態(tài)鏈,比如 Maven 或 Gradel,不懂吧?沒(méi)事,學(xué)!
IDE,比如 eclipse 或 IDEA Intellij,不懂吧?沒(méi)事,學(xué)!順便一提,知道沒(méi)接觸過(guò) Java 的人想跑起一個(gè)應(yīng)用來(lái)有多難嗎?我就是為此才愛(ài)上 Rails 的!
……
就這樣,為了調(diào)試 HTML+CSS,你最終變成除了不會(huì)寫(xiě) Java 代碼外其他全都會(huì)的 Java 開(kāi)發(fā)工程師。
你們這些從后端出身的家伙們能體會(huì)到前端頁(yè)面仔們邁出這一步需要多大的勇氣和毅力嗎?
你們能想象他們之所以不得不學(xué)做這些,就是因?yàn)槟銈儫o(wú)法認(rèn)真對(duì)待 HTML+CSS+JavaScript 嗎?
為什么要在后端的環(huán)境下做前端的事情?其實(shí)就為了三個(gè)字:擦屁股!
你可以說(shuō)我們這一群人都很菜,我也承認(rèn),可是你要知道:環(huán)境不是時(shí)時(shí)處處都可以給你各種選擇的,有時(shí)候你唯一能做的選擇就是改變自己。那么作為一個(gè)只懂 HTML+CSS+皮毛 JavaScript 的我,能做出什么?不知從何時(shí)開(kāi)始,“如果可以不再依賴任何環(huán)境就可以做好我們的份內(nèi)之事就好了”這樣幼稚的念頭開(kāi)始縈繞在我的腦袋里……
回到 Jeffery 的視頻教程,在其中的一節(jié)他演示了 Ajax 獲取遠(yuǎn)程數(shù)據(jù)然后動(dòng)態(tài)修改 DOM 的例子,當(dāng)時(shí)的例子里用的是 Twitter 的 API,然后每隔一段時(shí)間拉取幾條新數(shù)據(jù)讓頁(yè)面即時(shí)刷新這樣子……
不要笑,知道我當(dāng)時(shí)有多震驚嗎?我覺(jué)得我們就他媽的是一群傻逼好嗎?
第二天我慌不擇路的把這段視頻拿給后端架構(gòu)師看,問(wèn)他實(shí)現(xiàn)這樣的東西,可行?他憋了半天:我們都是直接去數(shù)據(jù)渲染到 JSP 的,API 沒(méi)做過(guò)……
操!沒(méi)做過(guò)難道不能做?我趕緊拋出了誘餌:如果搞的出來(lái),以后你們?cè)僖膊挥锰啄0媪耍?/p>
這貨立馬答應(yīng)了……
此后就是翻天覆地的折騰,我搜遍了所有能找到的資料,把它們翻成中文或者直接當(dāng)面講給后端聽(tīng),有些東西我們都無(wú)法理解就先記下來(lái),晚上回去我上 SO 問(wèn),上 Youtube 搜會(huì)議等資料看。
然后他們告訴我,如果換 Spring 的話可能會(huì)比較簡(jiǎn)單,因?yàn)樗麄兡馨俣鹊接?Spring 開(kāi)發(fā) API 的例子。于是我們就開(kāi)始改造了。
改造的第一步是不用寫(xiě) JSP(或者少量的寫(xiě)),但是靜態(tài)資源其實(shí)還是放在 Tomcat 容器里的,因?yàn)槲覀兘?jīng)過(guò)嘗試發(fā)現(xiàn)跨域問(wèn)題解決不了(是的,當(dāng)時(shí)就是菜,連反向代理都不懂),不過(guò)沒(méi)關(guān)系,反正我已經(jīng)學(xué)會(huì)了本地跑 Tomcat 了,至少我們可以不用寫(xiě) JSP 了嘛?,F(xiàn)在回想一下當(dāng)初搞前后分離的原始動(dòng)機(jī)竟然就是為了不再去寫(xiě) JSP,多么滑稽啊!然而反過(guò)來(lái)想想,這也映襯了一個(gè)事實(shí):前端工程師們的生態(tài)環(huán)境是有多糟糕!
再然后就是把 jQuery 修煉到滿級(jí)開(kāi)始無(wú)腦刷副本的無(wú)聊過(guò)程,當(dāng)然在這個(gè)過(guò)程中也體驗(yàn)了一些新東西,比如前端的模版引擎(Jade/Handlebars/Art等),模塊系統(tǒng)(SeaJS/RequireJS)等等,JavaScript 的水平和理解有了長(zhǎng)足的進(jìn)步,終于開(kāi)始有一個(gè)工程師的樣子了。
再之后就是大家都知道的劇本,node.js 橫空出世,一下子 HTTP Server,API Service,Shell Scripting……等等這些統(tǒng)統(tǒng)都可以用 JavaScript 來(lái)搞了,npm bower grunt gulp……等等這些應(yīng)運(yùn)而生,忽然間前端開(kāi)始有了自己的生態(tài)系統(tǒng)!盡管它還很弱小還很混亂,但是它給了我們這些野路子出身摸爬滾打渾身泥水的家伙們一道希望之光,它讓我們看到:
我們可以不依賴后端的運(yùn)行環(huán)境:node.js
我們可以有自己的生態(tài)圈:npm
我們可以隨心所欲使用各種方便的開(kāi)發(fā)工具:所以我后來(lái)成了 vim 黨
……
我們可以有很多可能,我們可以把我們擅長(zhǎng)的事情做得更棒而不需要后端哥哥們操心,我們可以省去很多后端要 cover 的工作讓他們專心寫(xiě)好自己的代碼,我們?cè)O(shè)想中的分離是有搞頭的,不僅僅是為了分離而分離,而是為了更好的專精、多能、協(xié)作、管理而分離!
何以如此狹隘的看待前后分離?時(shí)至今日我也不懂為什么有那么多人抱持著種種懷疑與偏見(jiàn)。
你們不用擔(dān)心數(shù)據(jù)層邏輯會(huì)有冗余,因?yàn)榘?Model 的邏輯分?jǐn)偟角岸松砩峡梢允∪ズ蠖说牟糠执a和處理工作,而前端也可以更容易地按照業(yè)務(wù)來(lái)組合自己需要的 Model
你們不用擔(dān)心視圖層的緩存,因?yàn)榉蛛x后前端只存在靜態(tài)資源,我們可以利用 CDN,利用 負(fù)載均衡,利用很多很多技術(shù)分?jǐn)傔^(guò)去必須讓后端來(lái)承擔(dān)的工作
你們不用擔(dān)心頁(yè)面渲染速度,首頁(yè)怕慢我們可以交給服務(wù)端來(lái)渲染,或者在中間加一個(gè)很簡(jiǎn)單的 node server 來(lái)做首頁(yè)靜態(tài)化渲染,后面的事情交給前端就是,只快不慢
你們不用擔(dān)心要為多個(gè)客戶端做不同的資源調(diào)度,只要 API 規(guī)劃得到,一套 Service 可以支持多個(gè)客戶端的業(yè)務(wù)體系,而前端行有余力甚至可以寫(xiě)出多個(gè)版本來(lái)做 A/B 測(cè)試
你們不用擔(dān)心 ……
優(yōu)點(diǎn)多了去了。
不是說(shuō)這些優(yōu)點(diǎn)目前都很成熟,也不是說(shuō)實(shí)現(xiàn)它們沒(méi)有代價(jià),但是你不去做就不可能成熟,代價(jià)也不是不可以有但關(guān)鍵是要看長(zhǎng)遠(yuǎn)的收益。
很現(xiàn)實(shí)的例子就是我們有一套系統(tǒng)本來(lái)是為自己做的,后來(lái)讓客戶知道了覺(jué)得很感興趣希望為自己定制一份。我們分析了一下,發(fā)現(xiàn)現(xiàn)有的 API 已經(jīng)可以滿足用戶的需求,只需要針對(duì)幾個(gè)具體的業(yè)務(wù)邏輯再擴(kuò)充幾個(gè)接口讓數(shù)據(jù)負(fù)載更合理便可,于是我們只用了三天就給客戶出了一個(gè)完全可用且相當(dāng)穩(wěn)定的 demo??蛻粲X(jué)得滿意,開(kāi)始按照他們的 VI 重新設(shè)計(jì)一套 UI,然后剩下的事就是找?guī)讉€(gè)頁(yè)面仔把頁(yè)面寫(xiě)出來(lái),現(xiàn)成的 Angular 邏輯往上一套小改幾處即可。
你會(huì)覺(jué)得不值得?
當(dāng)然了,我在這里不是鼓吹前后分離信仰,不是強(qiáng)求所有的事情都需要分離來(lái)做。一個(gè)產(chǎn)品的軌跡是需要產(chǎn)品研發(fā)團(tuán)隊(duì)自己把控的,如果人云亦云流行什么用什么那也就和無(wú)腦兒沒(méi)啥區(qū)別了。有些場(chǎng)景也的確不需要分離,比如說(shuō)門(mén)戶網(wǎng)站,CMS,Mini Site 這類的需求就可以沿用成熟的開(kāi)發(fā)體系。不過(guò)我之前談到過(guò),探索和實(shí)踐分離體系還有一個(gè)重要的好處,就是能夠讓你現(xiàn)有的前端開(kāi)發(fā)團(tuán)隊(duì)摸索和整理出一套單兵作戰(zhàn)的環(huán)境體系,即便是不用分離架構(gòu),我單純用 node.js 寫(xiě)一套門(mén)戶網(wǎng)站,CMS,Mini Site 這樣的東西也不會(huì)比 Rails 慢?。∵@樣一來(lái),我還是可以把后端的資源用在更重要的底層服務(wù)或業(yè)務(wù)邏輯去,把那些和頁(yè)面 UI 交互相關(guān),但又和數(shù)據(jù)層有著小小關(guān)聯(lián)的業(yè)務(wù)交給前端獨(dú)立完成,又有什么不好呢?
前后分離=SPA?SPA=臃腫框架?
這一點(diǎn)我覺(jué)得有必要分析清楚,標(biāo)題里的兩個(gè)問(wèn)號(hào)是我見(jiàn)到過(guò)最多的誤解。
首先,前后分離是架構(gòu)上的事情,第一次做肯定很痛苦,但做一遍之后好處還是很多的。舉實(shí)例說(shuō)明:
我們做過(guò)一個(gè)會(huì)議的應(yīng)用,這個(gè)應(yīng)用一開(kāi)始設(shè)計(jì)是沒(méi)有 web 端的前臺(tái)的,只有一個(gè)管理后臺(tái),前臺(tái)都是移動(dòng)端?;谶@個(gè)原因,我們還是分離的(因?yàn)槟愕锰峁?API 給移動(dòng)端,不分離還能怎么搞呢?),后臺(tái)用成熟的 Angular 很快就做好了。
沒(méi)想到后來(lái)有一個(gè)額外的要求,用戶要在創(chuàng)建會(huì)議的時(shí)候生成一套在線的會(huì)議手冊(cè),這個(gè)會(huì)議手冊(cè)就是一個(gè)簡(jiǎn)單的多頁(yè)面 CMS 系統(tǒng),當(dāng)用戶創(chuàng)建新會(huì)議的時(shí)候在后臺(tái)填寫(xiě)手冊(cè)相關(guān)的內(nèi)容,我們就要為它生成一系列的頁(yè)面來(lái)顯示(類似于 Mini Site),它有兩個(gè)特定要求:
不需要登錄,公開(kāi)訪問(wèn)。然而最初的設(shè)計(jì)是沒(méi)有賬號(hào)就不能參加會(huì)議,需要報(bào)名,所以我們后臺(tái)和移動(dòng) App 都是直接先要求登錄或注冊(cè)的,相應(yīng)的 API 請(qǐng)求也是如此,有鑒權(quán)控制的。
要能多端訪問(wèn),還要能嵌套在原生應(yīng)用的 webview 里,因?yàn)榧庸δ軄?lái)不及了,只有一天時(shí)間。
傳統(tǒng)的架構(gòu)你的寫(xiě)頁(yè)面然后套模版去調(diào)試,雖然只有不到10頁(yè),但也是很費(fèi)時(shí)間的。但我們已經(jīng)分離了,現(xiàn)在為了這么一個(gè)額外的需求也不值得再倒退回去,那么怎么做的呢?
單獨(dú)建一個(gè)會(huì)議手冊(cè)的項(xiàng)目;
模版用 Sass+Handlebars 很快搞定;
里面的接口請(qǐng)求為每一個(gè)會(huì)議服務(wù)商綁定一個(gè) token(后來(lái)還在后臺(tái)允許管理員重新生成和綁定 token),渲染頁(yè)面時(shí)寫(xiě)死在 <meta> 標(biāo)簽里(就好像 CSRF 的處理),以此繞過(guò)鑒權(quán)
寫(xiě)一個(gè)簡(jiǎn)單的 node service,就干一件事:渲染handlebars模版
創(chuàng)建會(huì)議的時(shí)候,Java API 傳會(huì)議 ID 給 node service,把渲染好后的頁(yè)面單獨(dú)保存在靜態(tài)資源服務(wù)器下(用 ID 創(chuàng)建獨(dú)立目錄),然后返回調(diào)用地址
后臺(tái)收到會(huì)議地址,嵌入一個(gè) iframe 做手冊(cè)預(yù)覽
一天搞定,完事。值得一提的是,整個(gè)手冊(cè)用了許多 HTML5 的新特性,比如 History API,SessionStorage,OfflineCache,GeoLocation,DesktopNotification,沒(méi)有用 Polyfill,因?yàn)檫@些都是可選特性,不支持就不作用,關(guān)鍵是:從頭到尾就是沒(méi)用 jQuery——不是我跟 jQuery 過(guò)不去,的確用不著,還嫌大。更不要提 SPA 框架了,完全沒(méi)有。
所以你看,分離架構(gòu)可以讓我們很快完成這樣的小任務(wù),并且可以單獨(dú)維護(hù)管理,也可以直接共享現(xiàn)有的 API 資源,它不一定只是為了 SPA 才分離,而且也沒(méi)有什么技術(shù)難度。能用很短的時(shí)間完成還能保證質(zhì)量,是因?yàn)槲覀冇谐墒斓臉?gòu)建和CI,如果換成是當(dāng)初 JSP 那一套,光配置個(gè)本地環(huán)境就夠夠的了,其他我都不敢想象。
分離是架構(gòu)選擇,決定了你如何管理、分配與協(xié)調(diào)現(xiàn)有的資源,至于你分離后要做 SPA 還是其他模式的應(yīng)用那完全是你的自由,并不是捆綁一加一的強(qiáng)制性決策。去構(gòu)建一個(gè)分離體系當(dāng)然會(huì)有挫折有代價(jià),沒(méi)有人否認(rèn)這個(gè),然而一)這是可選的;二)你能否看到和利用它的好處。
至于 SPA 一定是臃腫的嗎?保持這種思想的我只能說(shuō)你目光所見(jiàn)過(guò)淺。相比十年前的 web 開(kāi)發(fā),我能說(shuō)現(xiàn)在 Rails 很臃腫嗎?別說(shuō)十年前了,就是今天一樣也有人說(shuō) Rails too heavy!你覺(jué)得呢?那又怎樣呢?還不是該用就用?水平高的自然知道拆分和減肥,連 Rails 自己都知道瘦身一個(gè) Rails API 出來(lái),你以為所謂“臃腫”是 SPA 框架的專利嗎?SPA 之所以臃腫是有兩個(gè)主要的現(xiàn)階段環(huán)境因素決定的:
非常多的新特性層出不窮,為我們開(kāi)發(fā)更豐富強(qiáng)大的應(yīng)用程序提供了武器和彈藥。但是瀏覽器(及其他運(yùn)行環(huán)境)和設(shè)備碎片化的問(wèn)題導(dǎo)致這些新特性無(wú)法提供始終一致的表現(xiàn)或性能,于是各種框架就要在底層做兼容性的補(bǔ)充與改良,順便還要為尚未形成標(biāo)準(zhǔn)的新特性重新封裝 API 接口。比如說(shuō) Ember 干嘛要造一個(gè) Object 接口出來(lái)?不就是因?yàn)?Observable 接口沒(méi)有嗎?有什么大不了的?ES2016就有了(非??赡埽蛘吣憧梢圆挥?Ember 自己的,用第三方的 Observable 組件來(lái)代替也行。
jQuery 做的事情和這有多大區(qū)別?沒(méi)錯(cuò),jQuery 是相對(duì)輕了,可是它負(fù)責(zé)的面兒也少啊,哪位用 jQuery 的不都得附帶十個(gè)八個(gè)插件的?合在一起就輕了?
相對(duì)的,前端工程這塊業(yè)界整體的水平差距很大,牛的特牛,菜的特菜;但是菜的也希望用牛的工具,可又沒(méi)那個(gè)底蘊(yùn)解決牛的能解決的問(wèn)題,于是牛的就把一個(gè)一個(gè)特性統(tǒng)統(tǒng)封裝好聯(lián)系在一起,讓你盡可能快速簡(jiǎn)單的就能用到。
如果大部分的工程師都成長(zhǎng)起來(lái)了,也就沒(méi)有必要非得搞大而全的方案了,React 及其生態(tài)體系不就是一個(gè)很好的例子嗎?不給你搞大而全,只給你搞小而專,你以為你把那一堆連起來(lái)用就不叫 SPA 了?幼稚!
再說(shuō)一遍,SPA 是一種產(chǎn)品的技術(shù)形態(tài),而不是特定某(幾)種框架下的產(chǎn)物,滿足這種技術(shù)形態(tài)的工具鏈可以臃腫也可以簡(jiǎn)潔,這是因?yàn)榄h(huán)境和人決定的。
Single Page Application,not Some Particular Application
前后分離還有一方面的作用。前端工程師都有一個(gè)普遍的特點(diǎn):你讓他們寫(xiě)個(gè)頁(yè)面信手拈來(lái),但是你讓他們負(fù)責(zé)一個(gè)完整的業(yè)務(wù)多半就得抓瞎。為什么?因?yàn)樗麄兲T(mén)。最近一兩年我開(kāi)始大量的面試和儲(chǔ)備新人,十有八九都是這樣的:HTTP?不懂!Ajax?懂?。阌X(jué)得合理嗎?)jQuery 請(qǐng)求 API?會(huì)!Promise 用過(guò)?……沒(méi)。換個(gè)說(shuō)法,deferred 對(duì)象?哦哦,見(jiàn)到過(guò)?。阌X(jué)得合理嗎?)
諸如此類的問(wèn)題屢見(jiàn)不鮮,讓我對(duì)前端這個(gè)行業(yè)的未來(lái)充滿憂慮。當(dāng)初我也是從一竅不通的菜鳥(niǎo)開(kāi)始,若那時(shí)沒(méi)有“一定要擺脫 JSP”的幼稚理想,我怎么可能通過(guò)摸索前后分離讓自己擁有今天這樣相對(duì)全面的見(jiàn)識(shí)和理解?我走過(guò)的路讓我明白,探索前后分離并不是像很多旁觀者說(shuō)的“為了分離而分離”,反而是“為了更好的理解 web 開(kāi)發(fā)這回事而分離”。
因?yàn)楫?dāng)你開(kāi)始摸索這條路,你就不得不面對(duì)許多根本性的問(wèn)題,拿跨域資源共享來(lái)說(shuō)吧,以前的架構(gòu)前端工程師是極少需要面對(duì)這種問(wèn)題的,但你只要一分離就必然會(huì)碰到,然后你就要去學(xué)諸如 JSONP,CORS,HTTP 協(xié)議,瀏覽器安全機(jī)制,PreFlight Request,反向代理等等技術(shù)細(xì)節(jié)??此萍又亓藢W(xué)習(xí)成本(要我說(shuō),這些原本應(yīng)該是學(xué)校的責(zé)任?。?,但作為同事,你希望你身邊做的是個(gè)只會(huì)“追求像素級(jí)還原”的頁(yè)面仔呢?還是對(duì)上述知識(shí)點(diǎn)有著扎實(shí)的理解和實(shí)踐經(jīng)驗(yàn)的工程師呢?
說(shuō)到這,就昨天有人在 SF 上問(wèn)了個(gè)問(wèn)題,大致是問(wèn):JavaScript 怎樣才算學(xué)好了?總覺(jué)得需要自己能寫(xiě)一個(gè)庫(kù)或框架出來(lái)才算學(xué)好了,大家怎么看?
我剛好和人吵完了架,靜下心想了想之后作出了如下回答:
少年苦練 10 年拳術(shù)欲下山揚(yáng)名立萬(wàn),路遇一使刀漢子,數(shù)招后不敵慘敗而歸……回山后找?guī)煾祮?wèn)話
“師傅,為何我苦練十年還會(huì)輸?”
“因?yàn)槟悴恢来蚣懿恢箍梢杂萌^。”
“可你也沒(méi)告訴我??!”
“你只說(shuō)要學(xué)拳法,又沒(méi)說(shuō)學(xué)打架!”
“那我不學(xué)拳法了,我要學(xué)打架!”
“那就不只是要學(xué)拳法了,打架想要贏就得十八般武藝都學(xué),你未必要門(mén)門(mén)精通,但你最起碼得有這些見(jiàn)識(shí)。除此之外,還得學(xué)挨打,學(xué)療傷,學(xué)逃跑,學(xué)追蹤,學(xué)暗器,學(xué)使毒……想贏?哪有那么簡(jiǎn)單的!”
“那我還能成拳法宗師嗎?”
“呵呵,如果你打架再也不會(huì)輸,誰(shuí)敢說(shuō)你不是宗師?”
這個(gè)寓言想表達(dá)的意思是不言而寓的,我很贊同這里一位朋友說(shuō)的:我們不應(yīng)該有前端后端之分,我們可以有專精之處,但是對(duì)于 web 開(kāi)發(fā)這回事該懂的都應(yīng)該要懂,否則你怎么可能打得贏?同理,如果說(shuō)后端工程師需要靠寫(xiě)頁(yè)面來(lái)了解前端的話,那么前端也應(yīng)該有類似的方式來(lái)了解后端做的一些事情。在這里探索前后分離就是一個(gè)很好的教學(xué)與實(shí)踐相結(jié)合的手段。沒(méi)有哪個(gè)頁(yè)面仔會(huì)甘于永遠(yuǎn)切圖寫(xiě)頁(yè)面,他們也很羨慕后端哥哥們大神般的風(fēng)騷,只是他們所處的環(huán)境造成了他們只知道數(shù)十年如一日的就懂切頁(yè)面了,如果能多給他們一些提攜與幫助,誰(shuí)敢說(shuō)他們以后不會(huì)成為江湖高手?
很多人拿工作忙,缺人手,創(chuàng)業(yè)公司求效率等借口來(lái)回避在技術(shù)道路上的探索和進(jìn)取,說(shuō)真的我個(gè)人非常非??梢岳斫?,我當(dāng)初所做的事情其實(shí)和創(chuàng)業(yè)什么的也沒(méi)多大區(qū)別,我們?nèi)耸忠埠芫o缺——今天我們只有三個(gè)人維護(hù)著四款前后分離架構(gòu)的中大規(guī)模產(chǎn)品,這些產(chǎn)品有 Saas 版本的,還有大大小小十幾個(gè)在客戶那里獨(dú)立部署的,你沒(méi)看錯(cuò)就三個(gè)人!一個(gè) Java 工程師,一個(gè)懂 Java 的前端工程師,再加上我這個(gè)什么都懂一點(diǎn)但什么都不專精的萬(wàn)金油。
我們做的還不夠好,但我們已盡力做到自己能做的最好,與我們這五年來(lái)碰到的風(fēng)風(fēng)雨雨相比較,探索前后分離這真的不算個(gè)大事兒好嗎?
作為前端工程師(并且是懂得和尊重后端開(kāi)發(fā)的),我很欣慰能活躍在這個(gè)時(shí)代,就像有人說(shuō)的:這是前端最好的時(shí)代,也是前端最壞的時(shí)代。然而歷史無(wú)數(shù)次證明:真金不怕火煉,英雄應(yīng)運(yùn)而生。那些后端語(yǔ)言環(huán)境和框架體系難道沒(méi)有經(jīng)歷過(guò)同樣的革新與變遷?就因?yàn)槲覀冞^(guò)去是只會(huì)寫(xiě) jQuery 的頁(yè)面仔,所以我們就應(yīng)該永遠(yuǎn)這樣停滯不前?
這就是我探索前后分離的過(guò)程和心得感想,主要是在離職前為過(guò)去五年做一個(gè)總結(jié)。寫(xiě)得比較凌亂也沒(méi)什么技術(shù)含量,根本的意思還是要鼓勵(lì)眾多的前端同行們:學(xué)校沒(méi)有我們的專業(yè)課,社會(huì)對(duì)我們的工作沒(méi)有準(zhǔn)確的認(rèn)知和評(píng)價(jià),這都不要緊!重要的是我們自己不能看輕自己的能力,不能放棄自己的價(jià)值。在學(xué)習(xí)和工作尚有余力的時(shí)候勇于探索吧,別管別人說(shuō)什么,本事學(xué)到手才是最重要的,要記?。耗闶且粋€(gè)工程師,你不是一個(gè)頁(yè)面仔!