Web開發(fā)模式的探索與思考

2018-06-09 16:01 更新

最近這幾年,隨著軟硬件技術(shù)的發(fā)展,Web開發(fā)的相關(guān)技術(shù)也在突飛猛進(jìn),跟十年之前甚至五年之前相比,無論是業(yè)務(wù)復(fù)雜度還是技術(shù)選型已經(jīng)完全是兩個(gè)維度。

本文是一篇隨筆,主要闡述一些作者個(gè)人對(duì)web開發(fā)模式的一些認(rèn)知和思考。

前言

在五、六年前,或者更早的時(shí)候,當(dāng)談及Web開發(fā)的時(shí)候,更多的是指代某一種以后端語言為主的技術(shù)棧,比如Java Web,Ruby on Rails,Php等技術(shù)棧。經(jīng)過最近幾年的web開發(fā)相關(guān)技術(shù)的迅速發(fā)展,現(xiàn)在說web開發(fā)可能會(huì)在不同的場(chǎng)景下會(huì)有不同的劃分。

典型的,所謂的Web前端、Web全棧等最近幾年才被逐漸細(xì)分出來的技術(shù)方向,正是在前面這種大環(huán)境下產(chǎn)生的。

言歸正傳,我們說Web開發(fā),那么什么Web開發(fā)?Web開發(fā)的本質(zhì)是什么?

如果在一個(gè)比較高的抽象層面來看這個(gè)問題,Web開發(fā)就是處理客戶端請(qǐng)求及服務(wù)端響應(yīng)這兩件事。

上圖就是一個(gè)非常高的級(jí)別的抽象。實(shí)際的開發(fā)中,還會(huì)有許多方面是需要考慮的。

我們來稍微豐滿一下這個(gè)抽象。

大概2008年左右,一個(gè)web應(yīng)用或者web開發(fā)的標(biāo)準(zhǔn)模型如下圖,

此時(shí),后端主要做的事情是從數(shù)據(jù)庫中拉取數(shù)據(jù),在server端生成html模板,然后將生成的模板發(fā)送到客戶端??蛻舳耸盏侥0搴?,在客戶端注入js和css,生成dom樹,然后渲染成頁面呈現(xiàn)給用戶。

這種模式現(xiàn)在依然在某些web應(yīng)用的開發(fā)中被使用。這種模式現(xiàn)在更多的被稱之為傳統(tǒng)開發(fā)模式,或者前后端混合開發(fā)模式。其主要的一個(gè)特點(diǎn)是,頁面模板由后端吐出,甚至吐出的模板會(huì)帶有樣式和交互。前端僅僅是將模板丟給瀏覽器渲染成dom樹生成頁面而已。有時(shí)候,前端也會(huì)注入一些js代碼為頁面的動(dòng)態(tài)交互提供支持。

這種開發(fā)模式,我本人是實(shí)際經(jīng)歷過的。以前JavaEE、JavaWeb比較火的時(shí)候,經(jīng)常會(huì)寫一個(gè)某某系統(tǒng),比如學(xué)生管理系統(tǒng)、圖書管理系統(tǒng)之類的,在server端通過java來操作JDBC鏈接數(shù)據(jù)庫,拿到需要的數(shù)據(jù),然后將數(shù)據(jù)與jsp組裝起來一起發(fā)送給客戶端,客戶端再交給瀏覽器渲染。后來還經(jīng)歷過php加smarty的開發(fā)模式。其實(shí)這兩種雖然使用的是不同的技術(shù)棧,但是開發(fā)模式的本質(zhì)是一樣的。

但是,這時(shí)候我們會(huì)發(fā)現(xiàn),同一個(gè)開發(fā)者會(huì)扮演好幾種角色,需要寫server端代碼,同時(shí)也需要寫模板層代碼,甚至還會(huì)寫一些js和css代碼。這里需要開發(fā)者在不同角色中切換。但是專注server端的開發(fā)者們往往非常討厭或者說不擅長寫模板、交互和樣式,這時(shí)候又發(fā)展出一種所謂套頁面的開發(fā)模式。所謂套頁面,是指先讓專職的前端開發(fā)切好靜態(tài)頁面,然后丟給后端開發(fā)去模仿然后寫出模板,將需要?jiǎng)討B(tài)變化的地方換成后端模板語法。這種模式已經(jīng)被證明是低效的,而且并沒有合理的應(yīng)用開發(fā)資源。

說到開發(fā)效率,在一個(gè)大體量的團(tuán)隊(duì)協(xié)作中,我們應(yīng)該秉承這樣一種思路,就是讓合適的人去合適的事,分工協(xié)作,流水線作業(yè)肯定是最優(yōu)的方案。

傳統(tǒng)開發(fā)模式下,我們的確有一些痛點(diǎn)需要解決。從而衍生出了這樣一種思路,讓前后端的開發(fā)分離開來,不同工種只負(fù)責(zé)各自的事情,然后遵循某一種協(xié)作約定,達(dá)到高效產(chǎn)出。

上面基本上就是前后端分離的演變歷史。

前后端分離的開發(fā)模式大概如下圖所示,

前后端分離開發(fā)模式下,server端從數(shù)據(jù)庫拿到元數(shù)據(jù),經(jīng)過處理后直接吐出數(shù)據(jù),而不是模板。client端拿數(shù)據(jù)之后,在客戶端端進(jìn)行模板渲染生成頁面呈現(xiàn)給用戶。

相比傳統(tǒng)開發(fā)模式,此時(shí)服務(wù)端不再處理模板層的業(yè)務(wù),而是直接只提供數(shù)據(jù)。職責(zé)更加單一。此時(shí)的服務(wù)端可能會(huì)根據(jù)不同業(yè)務(wù)需求或者架構(gòu)差異,還可能會(huì)直接提供服務(wù)。這里服務(wù)的含義往往指代的是微服務(wù)。甚至在一些非絕對(duì)的前后端分離模式下,服務(wù)端還會(huì)提供模板片段。

顯而易見,此模式下,前端開發(fā)需要做更多的事,而且可能會(huì)隨著業(yè)務(wù)的增長前端邏輯和代碼量會(huì)越來越龐大和復(fù)雜。這又推動(dòng)了各種前端框架的涌現(xiàn),比如Angular這種大而全的框架,比如React這種專注解決某一個(gè)層面業(yè)務(wù)的框架,比如Webpack這種彌補(bǔ)構(gòu)建功能缺失的框架,等等。各種框架本身又會(huì)帶動(dòng)其周邊社區(qū)的發(fā)展,使得整體前端開發(fā)圈子呈現(xiàn)技術(shù)爆炸式的發(fā)展。

除了傳統(tǒng)Web開發(fā)之外,因?yàn)橐苿?dòng)互聯(lián)網(wǎng)的快速發(fā)展,移動(dòng)端開發(fā)、微信開發(fā)、h5開發(fā)等等范疇,也被前端開發(fā)者們照單全收。還有,由于NodeJS平臺(tái)的崛起,前端開發(fā)可能已經(jīng)不僅僅限于瀏覽器端的開發(fā)工作,借助NodeJS平臺(tái),以及ExpressKoa等框架,前端開發(fā)者已經(jīng)具備了涉足服務(wù)端開發(fā)的能力。

這里有一篇文章,足以管中窺豹,前端開發(fā)的技術(shù)棧廣度和更新速度。

所以,可以探討的內(nèi)容實(shí)在是非常的多,本文不會(huì)過度發(fā)散,僅僅是探討Web開發(fā)相關(guān)的內(nèi)容。下面會(huì)將重心放在pc端的web開發(fā)模式的探討上,以前后端分離為出發(fā)點(diǎn),闡述本人經(jīng)歷過的兩種開發(fā)模式,以拋磚引玉。

中間層模式

前面有說過,因?yàn)镹odeJS平臺(tái)的活躍,特別是一些成熟的服務(wù)端開發(fā)框架的出現(xiàn),比如express、koa等,讓前端開發(fā)者使用javascript進(jìn)行服務(wù)端程序的開發(fā)成為可能,甚至還可以操作數(shù)據(jù)庫。這也給之前一直在瀏覽器端開發(fā)的前端開發(fā)者們開辟了新的工作場(chǎng)景,如果再掌握一些數(shù)據(jù)庫,http協(xié)議,網(wǎng)絡(luò)安全,web server等方面的知識(shí),那么我個(gè)人感覺前端開發(fā)者完全可以將自己的角色切換成一名后端開發(fā),處理一些通用場(chǎng)景下的后端開發(fā)應(yīng)該是沒有問題的。

顯而易見,這對(duì)傳統(tǒng)的前端開發(fā)者們提出了更高的要求,工作內(nèi)容涉足的領(lǐng)域相比之前而言更加底層,會(huì)更加頻繁的去和真正的后臺(tái)開發(fā)者進(jìn)行溝通,甚至?xí)⑴c一些約定和規(guī)范的制定,這時(shí)候又會(huì)要求我們能夠有一些后端思維,否則你跟別人在溝通事情的時(shí)候都不在頻道上,那怎么可以呢?!

這部分內(nèi)容的標(biāo)題是中間層模式,那么什么是中間層模式呢?我個(gè)人的理解是,以前后端分離為出發(fā)點(diǎn),借助NodeJS平臺(tái),在web后端與傳統(tǒng)前端(UI層)之間增加一層中間層,負(fù)責(zé)處理數(shù)據(jù)、模板、業(yè)務(wù)等內(nèi)容。它是后端與UI層的橋梁。有的人喜歡稱這個(gè)中間層為膠水層。

下面有一張我自己畫的圖,我覺得應(yīng)該可以表達(dá)出我想法中的所謂中間層模式,

  1. 首先,這里可能沒有明確意義上的后端,取而代之是REST Server、Micro Service等內(nèi)容。他們的本質(zhì)依然是對(duì)上層提供數(shù)據(jù)。這些內(nèi)容都屬于backend的范疇。
  2. backend的上層就是frontend。這里所謂的frontend其實(shí)是一種寬泛的指代,它表示了一個(gè)響應(yīng)用戶請(qǐng)求、交互等操作的集合。在這個(gè)集合中,最上層是用戶,然后是客戶端(瀏覽器)層,然后客戶端的的下面是NodeJS層。此外還會(huì)有一個(gè)nginx層。
  3. 第一種場(chǎng)景,當(dāng)用戶發(fā)起一個(gè)頁面請(qǐng)求時(shí),瀏覽端會(huì)首先接受這個(gè)請(qǐng)求,然后丟給nginx來代理,nginx根據(jù)請(qǐng)求的類型將請(qǐng)求轉(zhuǎn)發(fā)給對(duì)應(yīng)的NodeJS層服務(wù)。NodeJS層內(nèi)部會(huì)解析來自nginx層轉(zhuǎn)發(fā)的請(qǐng)求,執(zhí)行對(duì)應(yīng)的業(yè)務(wù)類,組裝數(shù)據(jù),最終輸出一個(gè)html模板給客戶端(瀏覽器),瀏覽器端拿到模板之后,注入客戶端的js和css代碼之后再渲染到成頁面,呈現(xiàn)給用戶。
  4. 第二種場(chǎng)景,用戶在頁面發(fā)起交互請(qǐng)求時(shí),比如點(diǎn)擊按鈕發(fā)起請(qǐng)求,此時(shí)這個(gè)請(qǐng)求最終會(huì)丟給NodeJS層,NodeJS層收到請(qǐng)求之后進(jìn)行處理,然后將響應(yīng)返回給瀏覽器。
  5. 基本上,絕大部分來自客戶端(UI層)的請(qǐng)求和交互都會(huì)在NodeJS層進(jìn)行處理,UI層與底層的REST服務(wù)或者微服務(wù)是隔離的。當(dāng)然也有特例,比如圖片驗(yàn)證碼這種場(chǎng)景,將會(huì)由瀏覽器直接請(qǐng)求底層REST服務(wù)或者微服務(wù)。比如,
  6. 很多時(shí)候,用戶通過url請(qǐng)求一個(gè)頁面時(shí),NodeJS層收到這個(gè)請(qǐng)求后,會(huì)同時(shí)發(fā)出多個(gè)REST請(qǐng)求,或者向多個(gè)微服務(wù)發(fā)出請(qǐng)求,待所有的REST請(qǐng)求都返回后,在NodeJS層進(jìn)行數(shù)據(jù)組裝,處理,清洗等操作,這其中可能會(huì)摻雜一些特定的業(yè)務(wù)需求。我們一般稱這一過程為REST Combo。比如,

通過上面的描述,這種模式下前端開發(fā)們將會(huì)處理三方面的事,

  1. nginx層,監(jiān)控請(qǐng)求及類型,轉(zhuǎn)發(fā)請(qǐng)求到對(duì)應(yīng)的NodeJS服務(wù)。
  2. UI層,瀏覽器端實(shí)現(xiàn),各種交互、樣式、布局等。
  3. NodeJS層,連接UI層和底層數(shù)據(jù)提供方的橋梁,同時(shí)可能會(huì)注入一些偶合具體業(yè)務(wù)的實(shí)現(xiàn)。

更進(jìn)一步,我們?cè)趯?shí)際項(xiàng)目中,可以將UI層和NodeJS層不必劃分太明確的界限,如下的目錄結(jié)構(gòu),

其中app目錄是NodeJS層代碼,public目錄是UI層代碼,dist是項(xiàng)目構(gòu)建之后的目錄。在項(xiàng)目部署時(shí),只會(huì)針對(duì)dist目錄。

除此之外,這種模式下,我們還有許多可以探索和嘗試的地方,比如

  1. UI層實(shí)現(xiàn)的技術(shù)選型其實(shí)是比較靈活的,可以用JQuery式的開發(fā)方式,也可以引入一些成熟的前端MV*框架(比如NG,React,VueJS等)來提升開發(fā)效率。引入這些框架之后,可能會(huì)帶來許多其他層面需要解決的問題,比如前端構(gòu)建、前端模塊化等等。
  2. NodeJS可以不吐出模板,而是直接吐出數(shù)據(jù)或者吐出模板片段和數(shù)據(jù)。將NodeJS當(dāng)成UI層的數(shù)據(jù)提供server。
  3. 共用UI層和NodeJS層的部分代碼,比如特定的業(yè)務(wù)代碼,邏輯判斷,表單校驗(yàn)等等。
  4. 可以將NodeJS層和UI層隔離構(gòu)建打包,也可以混合構(gòu)建打包。
  5. ……

中間層模式解決方案的實(shí)踐

這里我嘗試給一個(gè)中間層模式的解決方案sword-plus。它的定位并不是大而全的一站式解決方案,而是為了更便利的開發(fā)NodeJS層的一系列工具集合以及常用功能的提煉?;?a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" target="_blank">Koa程序,提供了日志記錄、模板渲染、請(qǐng)求訪問、路由解析、業(yè)務(wù)類抽象等功能。其內(nèi)部的各個(gè)功能組件存在一定程度上的偶合。所以使用sword-plus的前提是,引入NodeJS作為中間層;底層的框架選型為Koa;NodeJS層向上層提供組裝好數(shù)據(jù)的模板。

sword-plus包括以下幾個(gè)部件,Router、Pugger、Connector、Logger、Handler、SwordError。

Router用于解析koa的包裝后的請(qǐng)求。其中有兩個(gè)主要方法,

  1. parse,解析路由
  2. provider,生成業(yè)務(wù)類,并啟動(dòng)對(duì)應(yīng)路由的業(yè)務(wù)邏輯

Pugger主要用于組裝數(shù)據(jù)并渲染Jade/Pug模板。

  1. 這里的模板屬于服務(wù)端模板,在渲染的過程中,可通過參數(shù)配置是否記錄渲染日志。
  2. 主要方法render,用于組裝數(shù)據(jù)生成服務(wù)端模板。
  3. 渲染日志將會(huì)記錄在渲染過程中的任何報(bào)錯(cuò)信息。

Connector封裝了NodeJS層->REST層,UI層->NodeJS層的請(qǐng)求操作。內(nèi)部使用了node-fetch。同時(shí)允許在操作請(qǐng)求時(shí),記錄請(qǐng)求日志。它有兩個(gè)方法,

  1. get,執(zhí)行g(shù)et請(qǐng)求
  2. post,執(zhí)行post請(qǐng)求

Logger是日志組件,Logger將所有的日志分為如下幾大類(category),

  • fatal、error、warn、info
  • request、response、render、action

其中第一行其實(shí)是bunyan自帶日志等級(jí)的alias,第二行是根據(jù)不同業(yè)務(wù)場(chǎng)景抽象出來的。

  1. request,NodeJS程序向REST服務(wù)器發(fā)送rest api請(qǐng)求的日志
  2. response,REST服務(wù)器返回給NodeJS程序的rest api響應(yīng)的日志
  3. render,服務(wù)端模板的渲染日志,這里所謂的渲染日志其實(shí)是跟客戶端(瀏覽器)是沒有關(guān)系的,它僅僅表示模板文件和數(shù)據(jù)的組裝和編譯過程
  4. action,所有由用戶發(fā)起從而產(chǎn)生的交互日志,包括頁面請(qǐng)求、表單提交、客戶端ajax請(qǐng)求等等

每一條日志都是一個(gè)record抽象,每個(gè)record實(shí)例在category的維度下,還會(huì)有l(wèi)evel的區(qū)分,常用的level有如下幾種info、warn和error。

Handler是業(yè)務(wù)類模型的頂層抽象。所有請(qǐng)求經(jīng)過Router解析之后,都會(huì)通過Handler來派生出具體的業(yè)務(wù)類。在Handler中可以使用內(nèi)部擴(kuò)展或者外部拓展(Handler.inject),來增加所有業(yè)務(wù)類可使用的功能方法。此外,Handler中對(duì)幾個(gè)套件的實(shí)例對(duì)象做了代理,使得在具體的業(yè)務(wù)類中可以使用他們。Handler中提供如下幾個(gè)方法,

  1. inherits,在Router解析完畢之后,在Router.provider中通過此方法生成具體的業(yè)務(wù)類Clazz。
  2. dispatch,在業(yè)務(wù)類中對(duì)get和post請(qǐng)求類型進(jìn)行自適配轉(zhuǎn)發(fā)。
  3. inject,在server啟動(dòng)時(shí),可以根據(jù)需要注入外部拓展,一旦注入,則在所有生成的業(yè)務(wù)類中都可使用。

SwordError是SwordPlus的Error封裝。用于統(tǒng)一分配error。

目前SwordPlus Error的type有如下幾種,

  • LACK_OF_PARAMETER
  • INVALID_OF_PARAMETER
  • 404
  • ERROR_OF_MAKEDIR
  • SUFFIX_NOT_SUPPORT
  • SUFFIX_IS_REQUIRED
  • TIMEOUT
  • MODULE_NOT_FOUND
  • NO_TEMPLATE_FILE

sword-plus的執(zhí)行流程簡圖大致如下,

具體的使用可以參考sword-plus中的demo文件夾。

完全分離模式

這里稍微提一下所謂的完全分離模式,即此模式下的前端開發(fā)和傳統(tǒng)意義上的后端開發(fā)完全隔離開,不會(huì)涉及到中間層的開發(fā)。如下圖,

此時(shí),前端和后端通過ajax請(qǐng)求來交互,后端返回給前端數(shù)據(jù)??蛻舳说乃惺虑?,包括頁面渲染、交互、樣式、路由等等都是由前端自己來管理。此時(shí)前端開發(fā)往往會(huì)引入一個(gè)較為成熟的前端開源框架作為底層選型。這種開發(fā)模式有其獨(dú)特的適用場(chǎng)景,比如單頁應(yīng)用(Single Page Application)、企業(yè)內(nèi)部的某個(gè)管理系統(tǒng)等等。如果前端開發(fā)對(duì)底層選型的框架較為熟悉,往往開發(fā)速度非???,基本上在實(shí)際開發(fā)中遇到的一些問題都可以在框架社區(qū)中找到解決方案。

這種開發(fā)也有其短板的方面,往往隨著需求迭代,前端的代碼量會(huì)越來越多,前端的各種交互和模塊化管理會(huì)越來越重,越來越不好維護(hù)。還有一點(diǎn)就是可供選擇的方案非常多,有時(shí)候你會(huì)不知道到底選擇哪種方案好。

我個(gè)人的看法是,根據(jù)場(chǎng)景和需求來進(jìn)行實(shí)現(xiàn)方案和技術(shù)的選型,不要一味的追求新技術(shù)和潮流。目前前端圈子中流行的幾種主流框架,比如Angular,React,Vue等都會(huì)有其優(yōu)勢(shì)和弱勢(shì)的地方。比如,Angular本身是包羅萬象的框架,一旦熟悉起來開發(fā)速度非常快,但是其入門門檻低深入比較復(fù)雜,而且有一些場(chǎng)景是其先天不適合的。React只關(guān)注View層的處理,提供了一個(gè)非常好的思路告訴我們應(yīng)該如何做View和Data層的交互和更新,但是其現(xiàn)有的數(shù)據(jù)狀態(tài)管理方案比如Redux,我個(gè)人感覺一直都不是太好用。VueJS的作者吸收了市面上眾多框架的優(yōu)勢(shì),個(gè)人感覺它一直都在發(fā)展中,本人并沒有在生產(chǎn)環(huán)境中使用過,不作過多評(píng)價(jià)。

不過話說回來,個(gè)人認(rèn)為前端圈子目前的快速發(fā)展是非常好的,雖然新東西很多,解決同一個(gè)痛點(diǎn)的方案可能會(huì)有很多選擇,但是這并不影響我們?nèi)W(xué)習(xí)他們,去領(lǐng)悟它們的解決問題的思維,甚至去體驗(yàn)一下他們有坑的地方,至于到底要不要再生產(chǎn)環(huán)境使用,那是另外一回事了,所以我個(gè)人并不厭惡前端圈子的當(dāng)下的這種現(xiàn)狀。

總結(jié)

這篇文章的主旨意在闡述一些我個(gè)人對(duì)當(dāng)下web開發(fā)模式的認(rèn)知和探索,個(gè)人認(rèn)為文章中的中間層開發(fā)模式是一種萬金油的開發(fā)模式,除了文章中談到的內(nèi)容,我也給出了一些可供繼續(xù)探索下去的點(diǎn),我認(rèn)為中間層開發(fā)模式基本上可以適配任何需求下的web開發(fā)需求。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)