Web前後端分離:MVC簡介
我們要深刻理解前後端分離技術有一個重要的前提,那就是要把前後端分離技術認為是傳統的web應用裡的MVC設計模式的進一步演進。那麼我們首先來看看MVC的定義,下面的內容摘錄於維基百科的解釋,具體如下:
MVC模式(Model-View-Controller)是軟體工程中的一種軟體架構模式,把軟體系統分為三個基本部分:模型(Model)、檢視(View)和控制器(Controller)。
MVC模式最早由Trygve Reenskaug在1978年提出[1] ,是施樂帕羅奧多研究中心(Xerox PARC)在20世紀80年代為程式語言Smalltalk發明的一種軟體設計模式。MVC模式的目的是實現一種動態的程式設計,使後續對程式的修改和擴充套件簡化,並且使程式某一部分的重複利用成為可能。除此之外,此模式通過對複雜度的簡化,使程式結構更加直觀。軟體系統通過對自身基本部分分離的同時也賦予了各個基本部分應有的功能。專業人員可以通過自身的專長分組:
(控制器 Controller)- 負責轉發請求,對請求進行處理。
(檢視 View) – 介面設計人員進行圖形介面設計。
(模型 Model) – 程式設計師編寫程式應有的功能(實現演算法等等)、資料庫專家進行資料管理和資料庫設計(可以實現具體的功能)。
各類用於 Web應用開發的語言裡都有屬於自己的MVC框架,例如本人最熟悉的服務端語言java裡就有大名鼎鼎的struts2,springMVC的MVC應用 框架,我早期從事java的web開發時候認為這些MVC框架都是非常的博大精深,用途廣泛,但是當我逐漸轉向了web前端技術開發以後又覺得這些框架的 很多功能顯得那麼的多餘和累贅,因此我曾寫過一篇文章專門討論過這些問題,該文章的名字叫做《為什麼做java的web開發我們會使用struts2,springMVC和spring這樣的框架?》。
其實這篇 文章被寫的源頭就是在於我認為像struts2和springMVC這樣的框架做了太多瀏覽器本身就可以完成的工作,例如:頁面的渲染操作,因為服務端搶 了瀏覽器端的部分工作,這其實也就等於限制了web前端技術的深入運用,像很多前端的優化技術以及很多提升使用者體驗的技術就很難派上用場,之所以產生這些 問題,我認為傳統的MVC框架本質其實是一個服務端的MVC框架,雖然MVC設計模式裡的V即View檢視層是想把介面開發工作專業化,讓介面設計人員能專心於介面開發,但是傳統的MVC框架下的View層的本質卻是一個不折不扣的服務端技術。
我們以java的web開發裡jsp為例,JSP全名為Java Server Pages,中文名叫java伺服器頁面,其根本是一個簡化的Servlet設計,它是java裡動態網頁的技術標準, 這就說明jsp雖然看起來像html,其實它並不是真正的html,它需要被java的web容器進行解析轉化為瀏覽器可以解析的html頁面,然後通過 網路傳輸到瀏覽器後,瀏覽器才能正確的展示這個jsp頁面,其他web開發語言裡都有類似的動態網頁技術標準,但是不管什麼語言的動態網頁技術標準,我們 使用它時候就是讓web前端技術被服務端技術所綁架,這也就是為什麼每個招聘web前端工程師的崗位都要問你是否會java,php語言的源頭。但是隨著 網際網路的大發展,對web前端的要求是越來越專業化,web前端本身所包含的技術難度已經不亞於任何一個服務端語言開發難度,因此我們需要web前端更高 的專業化,而不希望web前端工程師被服務端技術束縛的更多而限制了自身能力的發展,這就導致前後端分離技術的出現。
不過前後 端分離技術的第一階段倒不是從改變view層即檢視層開始的,而是從連線客戶端和服務端的C層即控制層開始的,控制層既要作用於客戶端又要作用於服務端, 如果一個功能頁面是一個程式設計師從瀏覽器端一直寫到模型層,控制層也就不是什麼問題了,但是如果當我們想按MVC的設計思想,讓介面開發人員專注於頁面開 發,服務端開發人員專注於服務端開發,那麼這個時候控制層的歸屬問題就顯的非常重要了。在傳統的MVC框架裡,因為M層和C層是使用同樣的語言體系,因此 我們很自然會把M層和C層的開發工作都交由服務端開發人員完成,這個決定無可厚非,但是傳統的MVC框架裡V層和C層其本質也是同一個技術體系下的(例如 java的web開發裡的jsp本質就是個servlet),因此V層和C層也是緊耦合的,因此介面開發人員開發頁面時候如何沒有C層支撐,那麼這個頁面 其實是根本跑不起來的,如果前端開發人員這時候跑去寫寫C層即控制層的程式碼,這就打破了原有的橫向分工,這個時候控制層的編碼工作就會變得混亂而難以控 制,看到這裡有人一定會說既然控制層是屬於服務端的,那麼前端技術人員就等等服務端的開發進度,再不行就自己寫個mock模擬下服務端的控制層,聽到這種 建議,我相信不管是前端的還是服務端的技術人員都會頭腦發麻,第一反應就是這不是自找麻煩啊,還不如一個人全部搞定算了。由此第一階段的前後端分離技術方 案出現了,這個方案需要解決的問題就是如何能讓web前端技術人員和web服務端技術人員協同起來工作,合理的分工,換句話說就是按web前端和web服 務端角度如何能橫向的分解web的開發工作。
前後端分離的第一階段需要解決問題的核心就是控制層的歸屬問題,從技術角度而言就是控制層到底是應該和檢視層解耦比較合理還是跟模型層解耦比較合理的問題。那麼我們這裡先回顧下MVC設計模式裡對控制層的定義,維基百科裡的定義是:
(控制器 Controller)- 負責轉發請求,對請求進行處理。
不過這個解釋我認為並不全面,以java的web開發裡的控制層設計為例,我們發現控制層以溝通檢視層和模型層的角度而言,控制層其實主要完成三項具體的工作,它們分別是:
工作一:控制層起到一個路由的作用。客戶端請求到達控制層後,控制層根據請求內容將請求路由到服務端某個模型層進行處理,模型層將請求處理完畢後,會把響應結果返回給控制層,控制層在根據響應資訊路由到特定的頁面。
工作二:控制層起到一個報文資訊格式轉化的作用。這裡以java的web開發為例,瀏覽器的資料都是以http報文形式傳送給服務端,而控制層就是將http報文資訊解析成java的物件,當然也可以是java的基本資料型別,然後控制層把解析好的資訊傳遞給模型層進行處理。
工作三:傳統的MVC框架裡,控制層其實深入參入到了頁面渲染的操作。在 java的web開發裡的控制層不管如何被包裝,其本質就是一個servlet,而jsp頁面本質也是個serlvet,因此我們可以這麼理解 jsp,jsp就是以頁面開發的方式寫java,而servlet就是以java的方式寫頁面,所以我們可以在servlet裡以檔案流的方式輸出頁面, 也可以讓servlet跳轉到jsp頁面。
由上面的 論述裡我們發現,其實傳統MVC框架裡控制層和模型層的聯絡方式相對很簡單的,它們的聯絡主要是路由和報文格式的轉化上,而控制層與檢視層的聯絡除此之外 還多了一個頁面渲染,而頁面渲染本身應該是屬於瀏覽器的技術範疇,是瀏覽器技術不可分割的一部分,也是我上面內容裡詬病傳統MVC框架問題所在,如果控制 層承擔了頁面渲染工作,那麼控制層和檢視層的耦合度就變得非常高,要想將其解耦是十分困難,一般只有我們打破了現有MVC框架的技術體系才能完成,相比之 下,控制層與模型層的解耦就顯得容易多了。那麼控制層與模型層如何解耦呢?具體如下:
首先我們 來解決下報文格式轉化的問題,這個技術方案很簡單就是借鑑http統一報文格式的特點,我們為控制層和模型層定義一套統一的報文格式,例如我們定義控制層 和模型層都以map的資料型別進行資料傳遞,這個map裡有個專門的欄位用來定義被路由到的模型介面資訊,有個欄位專門儲存需要傳遞的資料,具體的設計方 案可以根據實際的業務需要來設計。
接下來就 是路由的問題了,在解決報文格式轉化問題的論述裡我講到要在統一報文格式裡專門定義一個欄位用來儲存該資料到底路由到哪個模型進行處理,不過這個欄位並不 能完全解決路由問題,因此我們需要模型層對控制層提供一個統一的介面,任何控制層與模型層的溝通都通過這個統一介面來完成,只不過不同請求報文組裝的內容 不一樣而已,而這個介面還有個重要職責就是解析報文裡的路由資訊,讓請求能被正確的路由到對應的模型介面所處理。當然這個介面的返回值最好也是一個統一的 報文格式,這樣控制層解析模型層的返回資料也會便利的多了。
由上所述,我們發現第一階段的前後端分離工作控制層應該歸屬於web前端,這麼做更加合理,也更加容易實現,其實之後進化版的前後端分離方案,控制層也都是屬於web前端,只不過形式不同而已,這個我在下一篇文章裡繼續討論。
第一階段前後端分離方案解決的核心就是讓控制層和模型層解耦,這個方案進一步演化一下,我們可以把控制層和檢視層獨立成一個web應用,模型層也獨立成一個web應用,兩個web應用之間通過遠端呼叫方式進行溝通,這個方案我在以前文章裡寫過,這篇文章的名字叫做《我設計的網站的分散式架構》。
這個進化 版的方案增加了系統開發的難度,因為我們需要增加網路通訊的程式設計以及遠端呼叫的實現,更麻煩的是我們還需要進行復雜的多執行緒程式設計,既然增加了開發的難度為 什麼我還要這麼做呢?首先我們通過應用分層,可以動態的調節web前端和web服務端的負載壓力,還可以在模型層之前提供一道安全屏障,不過被服務端綁架 的web前端在提升整個web應用負載能力這塊還是很有限的,其實這種做法的最大好處就是利於SOA框架的設計,也就是說這種架構我們可以為服務端的 SOA化提供有力的保障,因為控制層和模型層的解耦,可以讓模型層真正做到專注於業務,而不會再發生那種把業務邏輯寫到控制層的問題了從而降低程式碼的健壯 性。