無論前端也好,后端也好,都是整個軟件體系的一部分。軟件產(chǎn)品也是產(chǎn)品,它的研發(fā)過程也必然是有其目的。絕大多數(shù)軟件產(chǎn)品是追逐利潤的,在產(chǎn)品目標確定的情況下,成本有兩個途徑來優(yōu)化:減少部署成本,提高開發(fā)效率。
減少部署成本的方面,業(yè)界研究得非常多,比如近幾年很流行的“去IOE”,就是很典型的,從一些費用較高的高性能產(chǎn)品遷移到開源的易替換的產(chǎn)品集群,又比如使用Linux + Mono來部署.net應用,避開Windows Server的費用。
提高開發(fā)效率這方面,業(yè)界研究得更多,主要途徑有兩點:加快開發(fā)速度,減少變更代價。怎樣才能加快開發(fā)速度呢?如果我們的開發(fā)不是重新造輪子,而是 每一次做新產(chǎn)品都可以利用已有的東西,那就會好很多。怎樣才能減少變更代價呢?如果我們能夠理清模塊之間的關系,合理分層,每次變更只需要修改其中某個部 分,甚至不需要修改代碼,僅僅是改變配置就可以,那就更好了。 我們先不看軟件行業(yè),來看一下制造行業(yè),比如汽車制造業(yè),他們是怎么造汽車的呢?造汽車之前,先設計,把整個汽車分解為不同部件,比如輪子,引擎,車門, 座椅等等,分別生產(chǎn),最后再組裝,所以它的制造過程可以較快。如果一輛汽車輪胎被扎破了,需要送去維修,維修的人也沒有在每個地方都修一下,而是只把輪胎 拆下來修修就好了,這個輪胎要是實在壞得厲害,就干脆換上個新的,整個過程不需要很多時間。
席德梅爾出過一款很不錯的游戲,叫做《文明》(Civilization),在第三代里面,有一項科技研究成功之后,會讓工人工作效率加倍,這項科技的名字就叫做:可替換部件(Replacement Parts)。所以,軟件行業(yè)也應當引入可替換的部件,一般稱為組件。
在服務端,我們有很多組件化的途徑,像J2EE的Beans就是一種。組件建造完成之后,需要引入一些機制來讓它們可配置,比如說,工作流引擎,規(guī) 則引擎,這些引擎用配置的方式組織最基礎的組件,把它們串聯(lián)為業(yè)務流程。不管使用什么技術、什么語言,服務端的組件化思路基本沒有本質差別,大家是有共識 的,具體會有服務、流程、規(guī)則、模型等幾個層次。
早期展示層基本以靜態(tài)為主,服務端把界面生成好,瀏覽器去拿來展示,所以這個時期,有代碼控制的東西幾乎全在服務端,有分層的,也有不分的。如果做了分層,大致結構就是下圖這樣:
這個圖里,JSP(或者其他什么P,為了舉例方便,本文中相關的服務端技術都用Java系的來表示)響應瀏覽器端的請求,把HTML生成出來,跟相 關的JavaScript和CSS一起拿出去展示。注意這里的關鍵,瀏覽器端對界面的形態(tài)和相關業(yè)務邏輯基本都沒有控制權,屬于別人給什么就展示什么,想 要什么要先提申請的尷尬局面。
這個時期的Web開發(fā),前端的邏輯是基本可忽略的,所以前端組件化方式大同小異,無論是ASP還是JSP還是其他什么P,都可以自定義標簽,把HTML代碼和行間邏輯打包成一個標簽,然后使用者直接放置在想要的地方,就可以了。
在這一時代,所謂的組件化,基本都是taglib這樣的思路,把某一塊界面包括它的業(yè)務邏輯一起打成一個端到端的組件,整個非常獨立,直接一大塊從界面到邏輯都有,而且邏輯基本上都是在服務端控制,大致結構如下圖所示。
自從Web2.0逐漸流行,Web前端已經(jīng)不再是純展示了,它逐漸把以前在C/S里面做的一些東西做到B/S里面來,比如說Google和微軟的在線Office,這種復雜度的Web應用如果還用傳統(tǒng)那種方式做組件化,很顯然是行不通的。
我們看看之前這種組件化的方式,本質是什么?是展現(xiàn)層跟業(yè)務邏輯層的隔離,后端在處理業(yè)務邏輯,前端純展現(xiàn)。如果現(xiàn)在還這么劃分,就變成了前端有界 面和邏輯,后端也有邏輯,這就比較亂了。我們知道,純邏輯的分層組件化還是比較容易的,任何邏輯如果跟展現(xiàn)混起來,就比較麻煩了,所以我們要把分層的點往 前推,推到也能把單獨的展現(xiàn)層剝離出來。
如下圖所示,因為實際上HTML、CSS、JavaScript這些都逐漸靜態(tài)化,所以不再需要把它們放在應用服務器上了,我們可以把它們放在專門 的高性能靜態(tài)服務器上,再進一步發(fā)展,就可以是CDN(Content Delivery Network,內(nèi)容分發(fā)網(wǎng)絡)。前端跟后端的通信,基本都是通過AJAX來,也會有一些其他的比如WebSocket之類,總之盡量少刷新了。
在這張圖里面可以看到,真正的前端已經(jīng)形成了,它跟應用服務器之間形成了天然的隔離,所以也能夠很獨立地進行一些發(fā)展演進。
現(xiàn)在很多Web程序在往SPA(單頁面程序,Single Page Application)的方向發(fā)展,這類系統(tǒng)通常比較類似傳統(tǒng)的C/S程序,交互過程比較復雜,因此它的開發(fā)過程也會遇到一些困難。
那為什么大家要做SPA呢?它有很多明顯的好處,最核心的優(yōu)勢就是高效。這個高效體現(xiàn)在兩個方面:一是對于用戶來說,這種方式做出來的東西體驗較 好,類似傳統(tǒng)桌面程序,對于那些需要頻繁操作的行業(yè)用戶,有很大優(yōu)勢。二是運行的效率較高,之前集成一些菜單功能,可能要用iframe的方式引入,但每 個iframe要獨立引入一些公共文件,服務器文件傳輸?shù)膲毫^大,還要初始化自己的一套內(nèi)存環(huán)境,比較浪費,互相之間也不太方便通信,一般要通過 postMessage之類的方式去交互。
有了SPA之后,比如一塊界面,就可以是一個HTML片段,用AJAX去加載過來處理之后放到界面上。如果有邏輯的JavaScript代碼,也可以用require之類的異步加載機制去運行時加載,整體的思路是比較好的。
很多人說,就以這樣的需求,用jQuery再加一個異步js加載框架,不是很足夠了嗎?這兩個東西用得好的話,也是能夠解決一些問題的,但它們處理 的并不是最關鍵的事情。在Web體系中,展現(xiàn)層是很天然的,因為就是HTML和CSS,如果只從文件隔離的角度,也可以做出一種劃分的方式,邏輯放在單獨 的js文件里,html內(nèi)部盡量不寫js,這就是之前比較主流的前端代碼劃分方式。
剛才我們提到,SPA開發(fā)的過程中會遇到一些困難,這些困難是因為復雜度大為提升,導致了一些問題,有人把這些困難歸結為純界面的復雜度,比如說, 控件更復雜了之類,沒有這么簡單。問題在于什么呢?我打個比方:我們在電腦上開兩個資源管理器窗口,瀏覽到同一個目錄,在一個目錄里把某個文件刪了,你猜 猜另外一個里面會不會刷新?
毫無疑問,也會刷新,但是你看看你用的Web頁面,如果把整個復雜系統(tǒng)整合成單頁的,能保證對一個數(shù)據(jù)的更新就實時反饋到所有用它的地方嗎?怎么做,是不是很頭疼?代碼組織的復雜度大為提高,所以需要做一些架構方面的提升。
提到架構,我們通常會往設計模式上想。在著名的《設計模式》一書中,剛開始就講了一種典型的處理客戶端開發(fā)的場景,那就是MVC。
傳統(tǒng)的MVC理念我們并不陌生,因為有Struts,所以在Web領域也有比較經(jīng)典的MVC架構,這里面的V,就負責了整個前端的渲染,而且是服務端的渲染,也就是輸出HTML。如下圖所示:
在SPA時代,這已經(jīng)不合適了,所以瀏覽器端形成了自己的MVC等層次,這里的V已經(jīng)變成客戶端渲染了,通常會使用一些客戶端的HTML模版去實現(xiàn),而模型和控制器,也相應地在瀏覽器端形成了。
我們有很多這個層面的框架,比如Backbone,Knockout,Avalon,Angular等,采用了不同的設計思想,有的是MVC,有的是MVP,有的是MVVM,各有其特點。
以Angular為例,它推薦使用雙向綁定去實現(xiàn)視圖和模型的關聯(lián),這么一來,如果不同視圖綁定在同一模型上,就解決了剛才所說的問題。而模型本身也通過某種機制,跟其他的邏輯模塊進行協(xié)作。
這種方式就是依賴注入。依賴注入的核心理念就是通過配置來實例化所依賴的組件。使用這種模式來設計軟件架構,會犧牲一些性能,在跟蹤調試的便利性等方面也會有所損失,但換來的是無與倫比的松耦合和可替代性。
比如說,這些組件就可以單獨測試,然后在用的時候隨手引入,毫無壓力。對于從事某一領域的企業(yè)來說,光這一條就足以吸引他在上面大量投入,把所有不常變動領域模型的業(yè)務代碼都用此類辦法維護起來,這是一種財富。
如果我們來設計Angular這么一個前端框架,應當如何入手呢?很顯然,邏輯的控制必須使用JavaScript,一個框架,最本質的事情在于它的邏輯處理方式。
我們的界面為什么可以多姿多彩?因為有HTML和CSS,注意到這兩種東西都是配置式的寫法,參照后端的依賴注入,如果把這兩者視為跟Spring框架中一些XML等同的配置文件,思路就豁然開朗了。
與后端不同的是,充當前端邏輯工具的JavaScript不能做入口,必須掛在HTML里才能運行,所以出現(xiàn)了一個怪異的狀況:邏輯要先掛在配置文 件(HTML)上,先由另外的容器(瀏覽器或者Hybird的殼)把配置文件加載起來,然后才能從某個入口開始執(zhí)行邏輯。好消息是,過了這一步,邏輯層就 開始大放異彩了。
從這個時候開始,框架就啟動了,它要做哪些事情呢?
這些是主線流程,還有一些支線,比如:
SPA的一個典型特征就是部分加載,界面的部件化也是其中比較重要的一環(huán)。界面片段在動態(tài)請求得到之后,借助模版引擎之類的技術,經(jīng)過某種轉換,放置到主界面相應的地方。所以,從這個角度來看,HTML的組件化非常容易理解,那就是界面的片段化和模板化。
JavaScript這個部分有好幾個發(fā)展階段。
JavaScript組件化的目標是什么呢,是清晰的職責,松耦合,便于單元測試和重復利用。這里的松耦合不僅體現(xiàn)在js代碼之間,也體現(xiàn)在js跟 DOM之間的關系,所以像Angular這樣的框架會有directive的概念,把DOM操作限制到這類代碼中,其他任何js代碼不操作DOM。
如上圖所示,總的原則是先分層次,層內(nèi)再作切分。這么做的話,不再存在之前那種端到端組件了,使用起來沒有原先那么方便,但在另外很多方面比較好。
這方面,業(yè)界也有很多探索,比如LESS,SASS,Stylus等。為什么CSS也要做組件化呢?傳統(tǒng)的CSS是一種扁平的文本結構,變更成本較 高,比如說想要把結構從松散改緊湊,需要改動很多。如果把實際使用的CSS只當作輸出結果,而另外有一種適合變更的方式當作中間過程,這就好多了。比如 說,我們把一些東西定義成變量,每個細節(jié)元素使用這些變量,當需要整體變更的時候,只需修改這些變量然后重新生成一下就可以了。
以上,我們討論了大致的Web前端開發(fā)的組件化思路,后續(xù)將闡述組件化之后的協(xié)作過程和管控機制。
聯(lián)系客服