1. 程式人生 > >Web應用的元件化開發

Web應用的元件化開發

基本思路

1. 為什麼要做元件化?

  無論前端也好,後端也好,都是整個軟體體系的一部分。軟體產品也是產品,它的研發過程也必然是有其目的。絕大多數軟體產品是追逐利潤的,在產品目標確定的情況下,成本有兩個途徑來優化:減少部署成本,提高開發效率。

  減少部署成本的方面,業界研究得非常多,比如近幾年很流行的“去IOE”,就是很典型的,從一些費用較高的高效能產品遷移到開源的易替換的產品叢集,又比如使用Linux + Mono來部署.net應用,避開Windows Server的費用。

  提高開發效率這方面,業界研究得更多,主要途徑有兩點:加快開發速度,減少變更代價。怎樣才能加快開發速度呢?如果我們的開發不是重新造輪子,而是每一次做新產品都可以利用已有的東西,那就會好很多。怎樣才能減少變更代價呢?如果我們能夠理清模組之間的關係,合理分層,每次變更只需要修改其中某個部分,甚至不需要修改程式碼,僅僅是改變配置就可以,那就更好了。我們先不看軟體行業,來看一下製造行業,比如汽車製造業,他們是怎麼造汽車的呢?造汽車之前,先設計,把整個汽車分解為不同部件,比如輪子,引擎,車門,座椅等等,分別生產,最後再組裝,所以它的製造過程可以較快。如果一輛汽車輪胎被扎破了,需要送去維修,維修的人也沒有在每個地方都修一下,而是隻把輪胎拆下來修修就好了,這個輪胎要是實在壞得厲害,就乾脆換上個新的,整個過程不需要很多時間。

  席德梅爾出過一款很不錯的遊戲,叫做《文明》(Civilization),在第三代裡面,有一項科技研究成功之後,會讓工人工作效率加倍,這項科技的名字就叫做:可替換部件(Replacement Parts)。所以,軟體行業也應當引入可替換的部件,一般稱為元件。

2. 早期的前端怎麼做元件化的?

  在服務端,我們有很多元件化的途徑,像J2EE的Beans就是一種。元件建造完成之後,需要引入一些機制來讓它們可配置,比如說,工作流引擎,規則引擎,這些引擎用配置的方式組織最基礎的元件,把它們串聯為業務流程。不管使用什麼技術、什麼語言,服務端的元件化思路基本沒有本質差別,大家是有共識的,具體會有服務、流程、規則、模型等幾個層次。

  早期展示層基本以靜態為主,服務端把介面生成好,瀏覽器去拿來展示,所以這個時期,有程式碼控制的東西幾乎全在服務端,有分層的,也有不分的。如果做了分層,大致結構就是下圖這樣:

圖1 Web 1.0

  這個圖裡,JSP(或者其他什麼P,為了舉例方便,本文中相關的服務端技術都用Java系的來表示)響應瀏覽器端的請求,把HTML生成出來,跟相關的JavaScript和CSS一起拿出去展示。注意這裡的關鍵,瀏覽器端對介面的形態和相關業務邏輯基本都沒有控制權,屬於別人給什麼就展示什麼,想要什麼要先提申請的尷尬局面。

  這個時期的Web開發,前端的邏輯是基本可忽略的,所以前端元件化方式大同小異,無論是ASP還是JSP還是其他什麼P,都可以自定義標籤,把HTML程式碼和行間邏輯打包成一個標籤,然後使用者直接放置在想要的地方,就可以了。

  在這一時代,所謂的元件化,基本都是taglib這樣的思路,把某一塊介面包括它的業務邏輯一起打成一個端到端的元件,整個非常獨立,直接一大塊從介面到邏輯都有,而且邏輯基本上都是在服務端控制,大致結構如下圖所示。

圖2 Web 1.0元件化

3. SPA時代,出現了新問題

  自從Web2.0逐漸流行,Web前端已經不再是純展示了,它逐漸把以前在C/S裡面做的一些東西做到B/S裡面來,比如說Google和微軟的線上Office,這種複雜度的Web應用如果還用傳統那種方式做元件化,很顯然是行不通的。

  我們看看之前這種元件化的方式,本質是什麼?是展現層跟業務邏輯層的隔離,後端在處理業務邏輯,前端純展現。如果現在還這麼劃分,就變成了前端有介面和邏輯,後端也有邏輯,這就比較亂了。我們知道,純邏輯的分層元件化還是比較容易的,任何邏輯如果跟展現混起來,就比較麻煩了,所以我們要把分層的點往前推,推到也能把單獨的展現層剝離出來。

  如下圖所示,因為實際上HTML、CSS、JavaScript這些都逐漸靜態化,所以不再需要把它們放在應用伺服器上了,我們可以把它們放在專門的高效能靜態伺服器上,再進一步發展,就可以是CDN(Content Delivery Network,內容分發網路)。前端跟後端的通訊,基本都是通過AJAX來,也會有一些其他的比如WebSocket之類,總之儘量少重新整理了。


圖3 Web 2.0

  在這張圖裡面可以看到,真正的前端已經形成了,它跟應用伺服器之間形成了天然的隔離,所以也能夠很獨立地進行一些發展演進。

  現在很多Web程式在往SPA(單頁面程式,Single Page Application)的方向發展,這類系統通常比較類似傳統的C/S程式,互動過程比較複雜,因此它的開發過程也會遇到一些困難。

  那為什麼大家要做SPA呢?它有很多明顯的好處,最核心的優勢就是高效。這個高效體現在兩個方面:一是對於使用者來說,這種方式做出來的東西體驗較好,類似傳統桌面程式,對於那些需要頻繁操作的行業使用者,有很大優勢。二是執行的效率較高,之前整合一些選單功能,可能要用iframe的方式引入,但每個iframe要獨立引入一些公共檔案,伺服器檔案傳輸的壓力較大,還要初始化自己的一套記憶體環境,比較浪費,互相之間也不太方便通訊,一般要通過postMessage之類的方式去互動。

  有了SPA之後,比如一塊介面,就可以是一個HTML片段,用AJAX去載入過來處理之後放到介面上。如果有邏輯的JavaScript程式碼,也可以用require之類的非同步載入機制去執行時載入,整體的思路是比較好的。

  很多人說,就以這樣的需求,用jQuery再加一個非同步js載入框架,不是很足夠了嗎?這兩個東西用得好的話,也是能夠解決一些問題的,但它們處理的並不是最關鍵的事情。在Web體系中,展現層是很天然的,因為就是HTML和CSS,如果只從檔案隔離的角度,也可以做出一種劃分的方式,邏輯放在單獨的js檔案裡,html內部儘量不寫js,這就是之前比較主流的前端程式碼劃分方式。

  剛才我們提到,SPA開發的過程中會遇到一些困難,這些困難是因為複雜度大為提升,導致了一些問題,有人把這些困難歸結為純介面的複雜度,比如說,控制元件更復雜了之類,沒有這麼簡單。問題在於什麼呢?我打個比方:我們在電腦上開兩個資源管理器視窗,瀏覽到同一個目錄,在一個目錄裡把某個檔案刪了,你猜猜另外一個裡面會不會重新整理?

  毫無疑問,也會重新整理,但是你看看你用的Web頁面,如果把整個複雜系統整合成單頁的,能保證對一個數據的更新就實時反饋到所有用它的地方嗎?怎麼做,是不是很頭疼?程式碼組織的複雜度大為提高,所以需要做一些架構方面的提升。

4. 架構的變更

  提到架構,我們通常會往設計模式上想。在著名的《設計模式》一書中,剛開始就講了一種典型的處理客戶端開發的場景,那就是MVC。

  傳統的MVC理念我們並不陌生,因為有Struts,所以在Web領域也有比較經典的MVC架構,這裡面的V,就負責了整個前端的渲染,而且是服務端的渲染,也就是輸出HTML。如下圖所示:

圖4 Struts MVC

  在SPA時代,這已經不合適了,所以瀏覽器端形成了自己的MVC等層次,這裡的V已經變成客戶端渲染了,通常會使用一些客戶端的HTML模版去實現,而模型和控制器,也相應地在瀏覽器端形成了。

圖5 SPA

  我們有很多這個層面的框架,比如Backbone,Knockout,Avalon,Angular等,採用了不同的設計思想,有的是MVC,有的是MVP(Model-View-Presenter, 模型-檢視-呈現),有的是MVVM(Model View ViewModel),各有其特點。

  以Angular為例,它推薦使用雙向繫結去實現檢視和模型的關聯,這麼一來,如果不同檢視繫結在同一模型上,就解決了剛才所說的問題。而模型本身也通過某種機制,跟其他的邏輯模組進行協作。

  這種方式就是依賴注入。依賴注入的核心理念就是通過配置來例項化所依賴的元件。使用這種模式來設計軟體架構,會犧牲一些效能,在跟蹤除錯的便利性等方面也會有所損失,但換來的是無與倫比的鬆耦合和可替代性。

  比如說,這些元件就可以單獨測試,然後在用的時候隨手引入,毫無壓力。對於從事某一領域的企業來說,光這一條就足以吸引他在上面大量投入,把所有不常變動領域模型的業務程式碼都用此類辦法維護起來,這是一種財富。

5. MV*框架的基本原理

  如果我們來設計Angular這麼一個前端框架,應當如何入手呢?很顯然,邏輯的控制必須使用JavaScript,一個框架,最本質的事情在於它的邏輯處理方式。

我們的介面為什麼可以多姿多彩?因為有HTML和CSS,注意到這兩種東西都是配置式的寫法,參照後端的依賴注入,如果把這兩者視為跟Spring框架中一些XML等同的配置檔案,思路就豁然開朗了。

  與後端不同的是,充當前端邏輯工具的JavaScript不能做入口,必須掛在HTML裡才能執行,所以出現了一個怪異的狀況:邏輯要先掛在配置檔案(HTML)上,先由另外的容器(瀏覽器或者Hybird的殼)把配置檔案載入起來,然後才能從某個入口開始執行邏輯。好訊息是,過了這一步,邏輯層就開始大放異彩了。

  從這個時候開始,框架就啟動了,它要做哪些事情呢?

  • 初始化自身(bootstrap)
  • 非同步載入可能尚未引入的JavaScript程式碼(require)
  • 解析定義在HTML上的規則(template parser)
  • 例項化模型(scope)
  • 建立模型和DOM的關聯關係(binding, injection)

  這些是主線流程,還有一些支線,比如:

  • 解析url的search字串,恢復狀態(route)
  • 載入HTML部件模板(template url)
  • 部件模板和模型的關聯(binding)

6. 如何做元件化

6.1. HTML的元件化

  SPA的一個典型特徵就是部分載入,介面的部件化也是其中比較重要的一環。介面片段在動態請求得到之後,藉助模版引擎之類的技術,經過某種轉換,放置到主介面相應的地方。所以,從這個角度來看,HTML的元件化非常容易理解,那就是介面的片段化和模板化。

6.2. JavaScript的元件化

  JavaScript這個部分有好幾個發展階段。

  • 早期的共享檔案,把公共功能的程式碼提出出來,多個頁面共用
  • 動態引用,消滅全域性變數
  • 在某些框架上進一步劃分,比如Angular裡面又分為provider,service,factory,controller

  JavaScript元件化的目標是什麼呢,是清晰的職責,鬆耦合,便於單元測試和重複利用。這裡的鬆耦合不僅體現在js程式碼之間,也體現在js跟DOM之間的關係,所以像Angular這樣的框架會有directive的概念,把DOM操作限制到這類程式碼中,其他任何js程式碼不操作DOM。


圖6 SPA元件化

  如上圖所示,總的原則是先分層次,層內再作切分。這麼做的話,不再存在之前那種端到端元件了,使用起來沒有原先那麼方便,但在另外很多方面比較好。

6.3. CSS的元件化

  這方面,業界也有很多探索,比如LESS,SASS,Stylus等。為什麼CSS也要做元件化呢?傳統的CSS是一種扁平的文字結構,變更成本較高,比如說想要把結構從鬆散改緊湊,需要改動很多。如果把實際使用的CSS只當作輸出結果,而另外有一種適合變更的方式當作中間過程,這就好多了。比如說,我們把一些東西定義成變數,每個細節元素使用這些變數,當需要整體變更的時候,只需修改這些變數然後重新生成一下就可以了。

  以上,我們討論了大致的Web前端開發的元件化思路,後續將闡述元件化之後的協作過程和管控機制。

管控平臺

  在上一篇中我們提到了元件化的大致思路,這一篇主要講述在這麼做之後,我們需要哪些外圍手段去管控整個開發過程。從各種角度看,面對較大規模前端開發團隊,都有必要建立這麼一個開發階段的協作平臺。

  在這個平臺上,我們要做哪些事情呢?

1. HTML片段

  我們為什麼要管理HTML片段?因為有介面要用它們,當這些片段多了之後,需要有個地方來管理起來,可以檢索、預覽它們,還能看到大致描述。

  這應該是整個環節中一個相對很簡單的東西,照理說,有目錄結構,然後剩下的就是單個的HTML片段檔案了,這就可以解決儲存和檢索的問題了,但我們還要考慮更多。

  已有的HTML片段,如何被使用呢?這肯定是一種類似include的方式,通過某種特殊標籤(不管是前端還是後端的方式)把這些片段引用進來,這時候就有了第一個問題:

  假設有介面A和介面B同時引用了片段C,在某個開發人員修改片段C內容的時候,他如何得知將會影響到介面A和B呢?一個比較勉強的方式是全專案查詢,但這在很多情況下是不夠的。

  如果我們的HTML片段是作為獨立的公共庫存在的,它已經不能通過專案內查詢去解決這一問題了,因為不管A還是B,只要他不處於片段C的專案空間,就無從追尋。

  這時候很多人會問兩個問題:

  1. 跨專案的介面片段重用,意義在哪裡?

    如果我們的產品是針對一個小領域,它的複雜度根本不需要劃分多個專案部分來協作完成。設想場景是面對很大的行業,各專案都是子產品,將來可能是其中若干個聯合部署,這時候,保持其中的一致性是非常重要的。比如我們有個基本配置介面,在多個子產品中都要用,如果各自開發一個,其操作風格很可能就是不一致的,給人的印象就是不專業。所以會需要把常見的介面片段都歸集起來,供業務方挑選使用。

  2. 修改C,只提供說明,但是不通知A和B,不實時更新他們的版本,然後自行決定怎樣升級,如何?

    這會有一個問題,每次有小功能升級的時候,程式碼是最容易同步合併的,所以才會有“持續整合”這個概念,如果是一直伴隨升級,總要比隔一個大階段才升級好,升級成本應儘量分攤到平時,就像農婦養小豬,小豬每天長一點,每天都抱來抱去,不覺得吃力,即使長大了也還能抱得動。

  現在問題就很明確了,一定要有一種方式來把這個依賴關係管理起來,很顯然,已有的版本庫是肯定管不了這些的,所以只能在外圍做一些處理。

  我們建立一個管理平臺,除了管理實體檔案的版本,還管它們之間的關係。具體這個關係如何收集整理,有兩種方式:手動配置,程式碼分析。

  手動配置是比較土的方式,開發人員每提交一個檔案,就去這系統上手動配置它的依賴關係。程式碼分析的話,要在每次提交檔案的時候解析檔案的包含規則,找出確切的檔案。這兩者各有利弊,前者比較笨,但容易做,後者對程式碼格式的要求比較高,要考慮的情況較多。

  我們的介面往往不是那麼簡單,HTML片段也可能有層次的,舉例來說:

界  面A裡面包含了片段B,但是片段B自身又包含了片段C,所以這個依賴關係也是有層級的,需要在設計的時候一併考慮。

2. JavaScript模組

  JavaScript程式碼的管理,比HTML片段的狀況好一些,因為業界很多這方面的解決方案。但它們還是沒有解決當依賴項產生變更的時候反向通知的問題。

  所以我們還是得像HTML片段一樣,把它們的依賴關係都管理到平臺裡。於是,每個JavaScript模組都顯式配置了自己所依賴的其他模組,通過這種單向關係,形成了一套完整的檢視。

  在JavaScript模組的程式碼實現中,我們是不提倡直接寫依賴關係的。很多通用規範,比如AMD,往往建議我們這樣寫模組:

define(['dep1', 'dep2'], function (dep1, dep2) {
    var moduleA = function () {};
    return moduleA;
});

  但我們的系統是面向行業的,比這種通用解決方案要苛刻一些。比如說,如果有一天重構程式碼,JavaScript模組們調整了目錄或者名字,這麼寫的就痛苦了,他必須把所有影響到的都去調整一遍,這是要搜尋替換的。況且,就像上面HTML模板的部分提到的,影響了處於其他專案中依賴它的程式碼,缺少合適的方式去通知他們修改。

  所以我們期望的是,在每個編寫的JavaScript模組中只存放具體實現,而把依賴關係放在我們的平臺上管理,這樣,即使當前模組作了改名之類的重構處理,處於外部專案中依賴它的那些程式碼也不必修改,下一次版本釋出的生成過程會自動把這些事情幹掉。

  對應到上面的這段程式碼,我們需要開發人員做的只是其中的實現,也就是moduleA的那個部分,外面這些依賴的殼子,是會在釋出階段根據已配置的依賴關係自動生成的。

  如果需要,JavaScript模組還可以細分,比如類似Angular裡面那樣,把factory,controller和directive分離出來,這會對後續有些處理提供方便。

  現在我們有必要討論一下模組的粒度了,我們這裡提到的都是基本的粒度,每個JavaScript模組中存放的應該只有一個很具體東西的實現。那麼,有個問題,在我們釋出的時候,是不是就按照這個粒度釋出出去呢?

  很顯然不行,如果這麼做,很可能會出現複雜介面一次要用10多個HTTP請求才能載入完它所需要的所有JavaScript程式碼的情況,所以需要做一些合併。

  那麼,合併的策略是什麼?在我們這個平臺上,開發人員又是要怎樣定義這個合併關係的呢?我們需要在模組之上定義一個更大粒度的組織方式,這個方式與模組的關係,就好比Java裡面,jar檔案與class的關係。如果開發人員不顯式配置,也可以通過全域性策略,比如按最下層目錄來合併。

  這個時候,在實際使用這些程式碼的時候,需要帶兩個配置資訊過去,一個是要動態載入的JavaScript檔案(合併之後的),二是每個JavaScript檔案中包含的原始模組。

3. 單元測試

  如果JavaScript模組都已經被良好有序管理起來,就可以為它們考慮單元測試的事情了。單元測試對於提高基礎單元的可靠度,是有非常重要意義的。

  在我們這個平臺裡,可以把單元測試跟JavaScript模組關聯起來,每個JavaScript模組可以掛一組單元測試程式碼,這些程式碼可以線上編寫,線上執行。

  單元測試的本質就是編寫模擬程式碼來呼叫已有模組,考慮到我們的模組是JavaScript,所以很多思路都傾向於在瀏覽器端執行它們,對於單個模組的單元測試,這不是個問題。

  如果要批量執行整個系統的單元測試,那就不一樣了。把JavaScript程式碼先載入到瀏覽器中,然後再執行,很多時候並不需要這麼複雜。我們完全可以在服務端把它們做了。

  藉助Node.js的能力,我們可以在服務端執行JavaScript程式碼,也就意味著能夠把絕大多數JavaScript模組的單元測試在服務端就執行掉。當然,我們為此可能要多做不少事情,比如說,有些庫需要移植一份node版的,常見的有AJAX呼叫等等。

  注意了,能夠在服務端做JavaScript單元測試是有先決條件的,程式碼的分層必須很良好,除了檢視層,其他任何層面都不能操作DOM。所以我們這裡主要測試的也正是除了檢視層之外的所有JavaScript業務邏輯。至於檢視層怎麼辦?這個真的很難解決,這世界上不是所有東西都能自動做的,只能先把可做的做了,以後再來考慮這些。

4. 文件和示例管理

4.1. 文件

  現在我們有HTML片段和JavaScript模組了,需要給它們多一些描述資訊。簡單描述顯然是不夠的,我們還要詳細文件。

  這種詳細文件可以通過某種方式生成,也可以由開發人員手動編寫。與傳統的離線文件不同,線上的文件更實時,並且,每當一個開發人員變更了他的文件之後,不需要經過全量構建,訪問者可以實時訪問到他的最新版本。

  熟悉GitHub的朋友們可能早已習慣這種方式,在專案庫裡面存在一些以md格式結尾的文字檔案,使用markdown語法來編寫一些說明文件。

  毫無疑問,這類格式很適合線上協作,所以我們也會在平臺上整合這麼一種編寫文件的方式,無論是針對HTML模板還是JavaScript模組,或者是其他什麼型別,甚至還可以用來當部落格,就像月影同學的gitpress平臺,能直接從GitHub上拉取文字或者HTML檔案形成部落格。

  文件除了以整合的形式瀏覽之外,應當也可以以單獨連結的方式發出去,這時候使用者就可以像看一個新聞網頁一樣去瀏覽。如果再進一步做下去,還可以做電子書的生成,提供打包的離線文件。

4.2. 示例

  在編寫程式碼文件的過程中,可能免不了要插入示例,示例有兩種形態,一種是純文字,類似gist這樣,一種是可線上執行,類似jsfiddle和jsbin這樣。

  這兩種都有各自的優點,所以可以都做,示例的存放可以與文件類似,也應當能通過一個連結獨立執行。

4.3. 幻燈片

  有時候我們看到一些線上的幻燈片,覺得效果很帥,比如reveal.js,我們的開發人員有時候作程式碼分析或者走查的時候也不免要寫一些演示,如果能把這些東西也隨專案管理起來,能線上檢視,會是很不錯的一件事。所以我們也可以考慮給它們加個儲存介面,甚至做個簡易的線上編寫器。

5. 專案與目錄管理

  說到現在,我們似乎還遺漏了一點什麼。那就是以上提到的這些東西,以什麼為組織單位來儲存?

  考慮到我們的這個平臺是要管理一整個大產品的全部前端內容的,它裡面應該分了很多專案,對應到子產品上,這麼一來,很自然地,專案就成了第一級組織單位。專案之下,沒有懸念地,只有目錄了。

  對於一個專案而言,它有哪些要做的事情呢?首先要能配置其實體儲存位置。前面提到的這麼多程式碼、文件之類,最終都是要實體儲存的,怎麼存?我們當然可以自己搞一套,在檔案系統上做起來,但是還要考慮它們的版本管理,非常麻煩,所以不如直接對接某個版本庫,呼叫它的介面去存取檔案,這裡配置的就是版本庫的路徑。

  其次,要考慮從已有專案複製,類似GitHub裡面的fork功能,不過內部處理機制可以略有不同,fork的專案預設未必要有實體檔案,只有當產生了修改或者新增操作的時候才建立,剩下的還引用原來的就可以了。我們這裡的專案複製功能是為專案化版本而考慮的,經常出現一個產品版本支援多個客戶專案的情況,所以可能會用得著這個特性。

  然後,也要考慮專案的依賴關係。依賴一個專案,意思是需要用到它裡面的元件,所以實質是元件的依賴。提供專案依賴這個檢視,只是為了未來變更的一些考慮。

6. 評論管理

  之前提到,我們整個平臺的目的是為了提高大型前端團隊的協作能力,協作是離不開交流的。上述的任何功能,都應當帶有交流溝通的能力。

  比如說,如果開發人員A使用了其他人寫的一個程式碼元件a,對其中一些細節有疑問,他應當可以對它進行評論。在他評論的時候,任何參與維護過這個元件的人員都能收到一個提醒,這時候他可以選擇過來看看,回覆這個疑問。同理,在文件、示例下也可以如此操作。

  在網際網路上有這類產品,用於在任意URL下掛接評論交流系統,比較有名的就是Disqus,我們可以看到很多網站下面掛著它,用於做交流評論,這樣使用者可以用一個賬號在多個網站之間交流。國內也有同類的,比如多說,能夠用微博、QQ等賬號登入進行交流。

  從我們這個平臺本身看,如果是部署在企業內部作流程提升,引入外部評論系統的可能性就比較小了。因為在企業內部用,一定是希望這個員工的賬號資訊跟工號掛鉤,也能夠跟版本伺服器賬號等模組作整合,許可權也便於控制。

  從另外一個角度講,某個人員登入這個系統的時候,他可能收到很多訊息,來自不同的程式碼或文件位置,挨個點過去回覆也有些麻煩,我們應當給他提供一個全域性檢視,讓他能在一個統一的介面把這些問題都答覆掉,如果他需要的話,也是可以點進去到實際的位置。

7. 使用者和許可權控制

  從以上部分我們已經看到,這個系統是一個比較複雜的開發過程管控平臺。這樣的話,每個使用的人就應當可以登入,然後分配不同的許可權等級。

  未登入使用者應當有一些東西的檢視許可權,但是不能發表評論。已登入的使用者根據許可權級別,可以控制能否建立、修改專案,建立、修改目錄,程式碼,單元測試,文件等。

8. 國際化字串管理

  一個跨語言區域的Web應用不可避免要跟國際化打交道,這個事情通常是在服務端做,比如通過在介面程式碼中嵌入類似<% =getRes(key, lan) %>這樣的程式碼,去獲取相應的字串,替換到介面裡來。

  這個事情是要佔用應用伺服器資源的,而且國際化本身其實是一個在執行之前就已經確定的事,完全可以把這個過程放在釋出階段就做掉。比如說,我們給每種語言預先就把程式碼生成多份,只是部署在一起,根據需要的情況來動態載入特定的那一份。

  有不少客戶端的國際化方案,是把資原始檔拆細,以頁面為單位儲存,但這其實是不太合理的。第一個原因就是在Web2.0時代,“頁面”這個概念本身就已經弱化了,到了單頁應用裡,整個應用都只是一個頁面,這個時候,資原始檔以什麼粒度來組織呢?

  我們提到過,採用MV*框架去做Web應用的架構,有一個目標是做元件化。元件化的意圖就是某個元件可以儘可能隨心所欲地放在需要的地方用。如果把資原始檔的粒度弄小到對應HTML片段和JavaScript模組這一級,靈活性倒是有了,帶來的問題就是管理成本增大。

  做一個行業應用,最重要的就是業務一致性,這包括邏輯的一致性,也包括了術語的一致性。某一個詞,可能在多個資原始檔中都出現,這就增加了不一致的可能性。

  所以,應當有一個統一的術語管理平臺,一切介面上出現的文字或者提示,都必須來自這個平臺。

9. 靜態資源的管理

  在釋出系統的時候,除了需要釋出程式碼,還需要釋出圖片等靜態資源,這些東西也應當被管理起來。

  靜態資源在兩種情況下可用:隨產品釋出,在本平臺被引用。比如說有一個圖片,在這個平臺上作了管理,它可以被配置到某個專案上,在釋出的時候匯出。這個圖片還可以被用連結的方式檢視或者下載,如果本平臺內部的一個文件或者示例要引用它,也是可以的。

10. 樣式與主題管理

  在Web系統裡,樣式和主題是很重要的一環。樣式的管理和釋出一直是一個比較複雜的話題,早幾年一般都是分塊寫,然後組合合併,最近這些年有LESS,SASS和Stylus這類技術,解決了編寫和釋出的分離問題。

  我們看看釋出的最大問題是什麼?是不同部分的合併。為了追求靈活性,不得不把東西拆得很細,之前HTML片段和JavaScript模組的處理方式都是這樣。這麼做,我們就需要另外一件事:這些細小的東西,儘可能要覆蓋全面。

  對應到CSS裡面,我們要做的是把每種在系統中可能出現的元素、類別都作為單獨的規則維護起來,生成一個全域性的規則列表。不同專案間,實現可以不同,但規則的名字是固定的,定製只允許修改實現,不允許修改規則。如果要新增之前沒有的規則,也必須在全域性規則列表裡先新增,再作實現。

  樣式規則被管理之後,可以在介面元件上對它作關聯,也可以不做。做的好處是釋出的時候能只把用到的那些樣式規則生成釋出出去,如果能接受每次釋出全量CSS,那也無所謂。

  除了規則,也需要考慮一些變數的管理,在CSS中合理使用變數,會大為減輕定製化所導致的工作量。

11. 一鍵釋出

  我們引入了這麼一堆東西,其實是增加了釋出的複雜度。為什麼呢?

  之前不管HTML、JavaScript還是CSS,都是手寫出來,最多經過一個minify的工作,就釋出了,整個過程很簡單,兩句指令碼搞定。

  現在可複雜了,先要分析依賴關係,然後提取檔案,然後國際化字串替換,然後合併,然後程式碼壓縮,整個過程很折騰,不給配置管理員一個解釋的話,他一定過來砍人。

  我們有個原則:解決問題的過程中,如果引入了新的問題,要求負責解決原問題的人也一起解決掉。現在為了一些意圖,增加了版本釋出的複雜度,那也要有個辦法再把這事擺平,至少不能比原來複雜。

  所以我們就要把這些過程都整合到管控平臺裡,做一個一鍵釋出的過程,把所有的這些操作都整合起來,配置管理員釋出版本的時候只要點一下就可以把所有這些事情做掉。甚至說,這些流程還可以配置,能夠加減環節。

  這時候我們做到了跟之前發版本一樣方便,能不能多做點什麼呢?

  可以把JavaScript單元測試整合到版本釋出階段。因為我們已經把JavaScript按照職責做了分層,並且把UI部分做了隔離,就可以在瀏覽器之外把這個單元測試做掉,平時提交程式碼的時候也可以做,最終在版本釋出階段再全量做一下,也是很有意義的。

  程式碼依賴關係管理的另一個目的是什麼呢?是最小化釋出,既然我們都管理了檔案之間的關係,那麼,從根出發,顯然是能夠得出哪些程式碼檔案在本專案中使用的,就可以每次從我們的全量程式碼庫中取得確切需要的一部分來發布。這也是我們整個管控平臺帶來的優勢。

12. 小結

  我們這一篇比較複雜,提出了一整套解決大規模前端協作的管控機制。這套理論的本質是在開發和版本釋出之間加了一個環節,把Web體系中除了服務之外的一切靜態資源都納入其中,強化了現有主流的一些基於命令列的前端工程化組織模式。

  相比於傳統行業,比如汽車製造,我們這個環節相當於生產流水線的設計,其中一些元件的儲存就類似倉儲機制,釋出就類似出廠過程。

  這個平臺本身還有不少其他的可做的東西,比如甚至可以在上面做介面的視覺化定製等,這些是長遠的終極目標,在後面的文章裡會談談一些考慮。

  後續文章中,我們會展望有了這個平臺之後,整個前端的協作流程是怎樣的。


本文參考自:http://blog.sina.com.cn/s/blog_696c5a5e0101g8sf.html