大規模的前端組件化與模塊化
本文根據Andrew Betts在QCon北京2014大會上的主題演講內容整理而成。
Andrew Betts是英國金融時報實驗室(FT Labs)的負責人,同時也是一位PHP和JavaScript程序員。他的團隊致力于研發試驗性質的Web技術并發布相關產品——比如金融時報Web App. 在加入金融時報實驗室之前,Andrew創建了Web咨詢公司Assanka,為諸如News International, The Economist Group and the FT這樣的客戶打造創新性的Web項目。
今天的話題是大規模的前端組件化與模塊化。首先,先介紹一下FT在研發方面面臨的挑戰:
不同的服務如搜索、內容、廣告、應用,都是由不同的團隊來開發,團隊之間的溝通較少。造成的結果就是,整個服務的靈活性和可維護性越來越差,系統變得越來越復雜之后,新人進來的學習難度很大。隨著整個軟件開發生命周期變得越來越復雜,新功能的集成變得越來越難;更糟糕的是,隨著移動設備的流行,研發團隊不得不把同樣一套邏輯分別在桌面端和移動端各自實現一次。這也是全世界的軟件研發團隊面對的挑戰。
為了應對這些問題,FT Labs開始推行幾條前端的開發理念,目前已經獲得比較好的效果。
***條理念是:“活的”風格指南。“活的”風格指南也可以理解為代碼即文檔,文檔即示例。這里舉一個例子,比如Facebook的React項目。你去看React項目的介紹頁,這個頁面本身就是一個React的推薦實現。
當然像React這樣的項目,說明頁面的實現是一個方面,此外還有另一個重點:組件化的開發方式。正如React不是一個框架——它提倡的是無框架,因為任何框架的引入都會增加額外的復雜度和學習成本。組件(Web components)則不同,它一旦開發出來,就是一個隨時可以很方便的調用的功能;而且,不同的團隊可以同時進行不同組件的開發而互不干擾。Web組件是一個正在快速發展中的特性,目前瀏覽器對它的支持還不***,不過也就是1-2年的時間,現在應該要為未來做準備。對于Web開發而言,向前兼容要比向后兼容更加重要。
組件化的使用在我們處理歷史網站的過程中節省了大量的工作。FT有超過600個域名需要維護,很多網頁從互聯網時代早期就開始運作。對于這些遺留頁面,要全都重寫以適應新的瀏覽器是代價高昂、不值得的,但你又要盡可能的讓它們能夠正常顯示。我們用組件來進行局部替換以解決這個問題。比如某個老頁面上有一個圖庫展示,現在的瀏覽器不能顯示了,你就批量把這種老舊的圖庫展示代碼替換成新的圖庫組件代碼即可。
另外還有一點很重要,就是擁抱模塊化,避免在代碼中嵌入依賴關系。做開發這行兒一個很重要的覺悟就是:你要相信,你現在寫出來的這些代碼,等兩年之后,你自己都會不想去看它。模塊化會讓你的生命更簡單。
對于瀏覽器兼容性,正如剛才所說,我們的建議是跟著***的瀏覽器功能走。不過這里面也有一個分界點,就是所謂core experience和primary experience的分界點,并盡可能的將分界點向擴大core experience的方向推進。對于NoJavaScript的處理,我們的經驗是,基本上所有人的瀏覽器都會支持JS的,盡可以放心大膽的用。
說到這里,我要進入今天的重頭了,那就是FT的Origami這個項目。Origami這個項目要做定義的話可以說是一套規范,是一組文檔化的***實踐,同時搭配Registry這套工具,以方便所有人以***實踐建立規范統一的服務和組件。
這套系統的構成大體上包括一套closure compiler,browserfy(+debowerfy&brfs),commonjs,sass(用于做css模塊化),taskrunner(基于grunt),以及bower。系統在設計上遵循幾個原則:
- 編譯時納入所有依賴
- 去中心化、分布式,比如我們的git repo是分散的,沒有一個所謂的core或common的codebase
- 內置命名和封裝的規則
對于上面提到的分界點,Origami是這樣處理的:core的部分需要保證在用戶環境對JavaScript支持差或不支持的情況仍然能夠完成基本內容的呈現、搜索引擎的抓取等。這個分界點在Origami當中通過 if (querySelector in document) 來實現判定。
Registry作為工具,會做以下幾個事情:
- 掃描所有已知的git服務器
- 給版本標簽建索引
- 給每個模塊的每個版本做build
- 把每個模塊的所有版本收集、整理到一個模塊頁面上
Registry可以說是我們Web服務的一個黃頁。我們把這個黃頁放在公開的互聯網上,這樣所有人都可以上去協作,每個人都可以查看每個組件的每個版本,它們的說明和相關文檔,實現的樣板等。如果有的功能還沒做完或者沒啟用,可以打上一個not implemented的flag。
大家可以在Registry上隨意查看我們的各個組件,比如header,footer,調色板,按鈕等。這些組件調用起來很簡單,以調色板為例,只需要
- <link rel="stylesheet" href="http://build.origami.ft.com/bundles/css?modules=o-colors@^2.3.8" />
- <script src="http://build.origami.ft.com/bundles/js?modules=o-colors@^2.3.8"'></script>
這樣的兩行代碼即可調用任意模塊的任意版本。
我們的build服務可以按需build不同模塊的任意組合,之后還進行打包、壓縮、優化等處理并通過CDN分發(經過了GZIP處理)。調用的時候可以指定調用指定的版本,或者自動調用***版。有了這套系統后,開發者創建原型變得非常簡單了。
下面我介紹一下我們遇到的一些零碎問題,以及我們是如何處理的。
首先,有關polyfill的加載,如何才能讓polyfill僅在需要的時候才加載?我們的處理辦法是在模塊的metadata中進行聲明,哪些是required,哪些是optional,以modernizr測試名來進行控制。
然后,如何在支持JS但是支持的不好的瀏覽器中顯示noscript當中的內容?我們的辦法是定義一個o-nojs-fallback類的div,通過類的visibility來控制呈現。
對于hover的處理,我們用了一個o-hoverable的組件來控制。
對于資源加載,為了確保每個組件都知道其他資源的URL地址,我們專門有一個o-asset組件。
***想說的是,將我們的這些工作公開在互聯網上,我們從中收獲了很多。