前端的第四階段

前端是一種技術問題較少、工程問題較多的軟件開發領域。php

當咱們要開發一款完整的Web應用時,前端將面臨更多的工程問題,好比:css

  • 大致量:多功能、多頁面、多狀態、多系統;
  • 大規模:多人甚至多團隊合做開發;
  • 高性能:CDN部署、緩存控制、文件指紋、緩存複用、請求合併、按需加載、同步/異步加載、移動端首屏CSS內嵌、HTTP 2.0服務端資源推送。

擴展閱讀:大公司裏怎樣開發和部署前端代碼?前端

這些無疑是一系列嚴肅的系統工程問題。git

前面講的三個階段雖然相比曾經「茹毛飲血」的時代進步很多,但用於支撐第四階段的多人合做開發以及精細的性能優化彷佛還欠缺點什麼。github

到底,缺什麼呢?ajax

進入第四階段,咱們只需作好兩件事就能大幅提高前端開發效率,而且兼顧運行性能,那就是——組件化開發與資源管理。算法

第一件事:組件化開發

分治的確是很是重要的工程優化手段。在我看來,前端做爲一種GUI軟件,光有JS/CSS的模塊化還不夠,對於UI組件的分治也有着一樣迫切的需求:json

如上圖,這是我所信仰的前端組件化開發理念,簡單解讀一下:後端

  1. 頁面上的每一個 獨立的 可視/可交互區域視爲一個組件;
  2. 每一個組件對應一個工程目錄,組件所需的各類資源都在這個目錄下就近維護
  3. 因爲組件具備獨立性,所以組件與組件之間能夠 自由組合
  4. 頁面只不過是組件的容器,負責組合組件造成功能完整的界面;
  5. 當不須要某個組件,或者想要替換組件時,能夠整個目錄刪除/替換。

其中第二項描述的就近維護原則,是我以爲最具工程價值的地方,它爲前端開發提供了很好的分治策略,每一個開發者都將清楚的知道,本身所開發維護的功能單元,其代碼必然存在於對應的組件目錄中,在那個目錄下能找到有關這個功能單元的全部內部邏輯,樣式也好,JS也好,頁面結構也好,都在那裏。瀏覽器

組件化開發具備較高的通用性,不管是前端渲染的單頁面應用,仍是後端模板渲染的多頁面應用,組件化開發的概念都能適用。組件HTML部分根據業務選型的不一樣,能夠是靜態的HTML文件,能夠是前端模板,也能夠是後端模板:

不一樣的技術選型決定了不一樣的組件封裝和調用策略。

基於這樣的工程理念,咱們很容易將系統以獨立的組件爲單元進行分工劃分:

因爲系統功能被分治到獨立的模塊或組件中,粒度比較精細,組織形式鬆散,開發者之間不會產生開發時序的依賴,大幅提高並行的開發效率,理論上容許隨時加入新成員認領組件開發或維護工做,也更容易支持多個團隊共同維護一個大型站點的開發。

結合前面提到的模塊化開發,整個前端項目能夠劃分爲這麼幾種開發概念:

名稱 說明 舉例
JS模塊 獨立的算法和數據單元 瀏覽器環境檢測(detect),網絡請求(ajax),應用配置(config),DOM操做(dom),工具函數(utils),以及組件裏的JS單元
CSS模塊 獨立的功能性樣式單元 柵格系統(grid),字體圖標(icon-fonts),動畫樣式(animate),以及組件裏的CSS單元
UI組件 獨立的可視/可交互功能單元 頁頭(header),頁尾(footer),導航欄(nav),搜索框(search)
頁面 前端這種GUI軟件的界面狀態,是UI組件的容器 首頁(index),列表頁(list),用戶管理(user)
應用 整個項目或整個站點被稱之爲應用,由多個頁面組成  

以上5種開發概念以相對較少的規則組成了前端開發的基本工程結構,基於這些理念,我眼中的前端開發就成了這個樣子:

示意圖 描述
整個Web應用由頁面組成
頁面由組件組成
一個組件一個目錄,資源就近維護
組件可組合,
組件的JS可依賴其餘JS模塊,
CSS可依賴其餘CSS單元

綜合上面的描述,對於通常中小規模的項目,大體能夠規劃出這樣的源碼目錄結構:

若是項目規模較大,涉及多個團隊協做,還能夠將具備相關業務功能的頁面組織在一塊兒,造成一個子系統,進一步將整個站點拆分出多個子系統來分配給不一樣團隊維護,針對這種狀況後面我會單開文章詳細介紹。

以上架構設計歷經許多不一樣公司不一樣業務場景的前端團隊驗證,收穫了不錯的口碑,是行之有效的前端工程分治方案。

吐槽:我本人很是反對某些前端團隊將前端開發劃分爲「JS開發」和「頁面重構」兩種崗位,更傾向於組件粒度的開發理念,對GUI軟件開發的分工規劃應該以功能爲單位,而不是開發語言;對開發者的技術要求也應該是掌握完整的端內技術。

第二件事:「智能」靜態資源管理

上面提到的模塊化/組件化開發,僅僅描述了一種開發理念,也能夠認爲是一種開發規範,假若你承認這規範,對它的分治策略產生了共鳴,那咱們就能夠繼續聊聊它的具體實現了。

很明顯,模塊化/組件化開發以後,咱們最終要解決的,就是模塊/組件加載的技術問題。然而前端與客戶端GUI軟件有一個很大的不一樣:

前端是一種遠程部署,運行時增量下載的GUI軟件

前端應用沒有安裝過程,其所需程序資源都部署在遠程服務器,用戶使用瀏覽器訪問不一樣的頁面來加載不一樣的資源,隨着頁面訪問的增長,漸進式的將整個程序下載到本地運行,「增量下載」是前端在工程上有別於客戶端GUI軟件的根本緣由。

上圖展現了一款界面繁多功能豐富的應用,若是採用Web實現,相信也是不小的體量,若是用戶第一次訪問頁面就強制其加載全站靜態資源再展現,相信會有不少用戶由於失去耐心而流失。根據「增量」的原則,咱們應該精心規劃每一個頁面的資源加載策略,使得用戶不管訪問哪一個頁面都能按需加載頁面所需資源,沒訪問過的無需加載,訪問過的能夠緩存複用,最終帶來流暢的應用體驗。

這正是Web應用「免安裝」的魅力所在。

由「增量」原則引伸出的前端優化技巧幾乎成爲了性能優化的核心,有加載相關的按需加載、延遲加載、預加載、請求合併等策略;有緩存相關的瀏覽器緩存利用,緩存更新、緩存共享、非覆蓋式發佈等方案;還有複雜的BigRender、BigPipe、Quickling、PageCache等技術。這些優化方案無不圍繞着如何將增量原則作到極致而展開。

因此我以爲:

第四階段前端開發最迫切須要作好的就是在基礎架構中貫徹增量原則。

相信這種貫徹不會隨着時間的推移而改變,在可預見的將來,不管在HTTP1.x仍是HTTP2.0時代,不管在ES5亦或者ES6/7時代,不管是AMD/CommonJS/UMD亦或者ES6 module時代,不管端內技術如何變遷,咱們都有足夠充分的理由要作好前端程序資源的增量加載。

正如前面說到的,第三階段前端工程缺乏點什麼呢?我以爲是在其基礎架構中缺乏這樣一種「智能」的資源加載方案。沒有這樣的方案,很難將前端應用的規模發展到第四階段,很難實現落地前面介紹的那種組件化開發方案,也很難讓多方合做高效率的完成一項大型應用的開發,並保證其最終運行性能良好。在第四階段,咱們須要強大的工程化手段來管理」玩具般簡單「的前端開發。

在個人印象中,Facebook是這方面探索的偉大先驅之一,早在2010年的Velocity China大會上,來自Facebook的David Wei博士就爲業界展現了他們使人驚豔的靜態網頁資源管理和優化技術。

David Wei博士在當年的交流會上提到過一些關於Facebook的一些產品數據:

  • Facebook整站有10000+個靜態資源;
  • 每一個靜態資源都有可能被翻譯成超過100種語言版本;
  • 每種資源又會針對瀏覽器生成3種不一樣的版本;
  • 要針對不一樣帶寬的用戶作5種不一樣的打包方法;
  • 有三、4個不一樣的用戶組,用於小批次體驗新的產品功能;
  • 還要考慮不一樣的送達方法,能夠直接送達,或者經過iframe的方式提高資源並行加載的速度;
  • 靜態資源的壓縮和非壓縮狀態可切換,用於調試和定位線上問題

這是一個狀態爆炸的問題,將全部狀態乘起來,整個網站的資源組合方式會達到幾百萬種之多(去重以後統計大概有300萬種組合方式)。支撐這麼大規模前端項目運行的底層架構正是魏博士在那次演講中分享的Static Resource Management System(靜態資源管理系統),用以解決Facebook項目中有關前端工程的3D問題(Development,Deployment,Debugging)。

那段時間 FIS 項目正好遇到瓶頸,當時的FIS仍是一個用php寫的task-based構建工具,那時候對於前端工程的認知度很低,以爲前端構建不就是幾個壓縮優化校驗打包任務的組合嗎,寫好流程調度,就針對不一樣需求寫插件唄,看似很是簡單。但當咱們支撐愈來愈多的業務團隊,接觸到各類不一樣的業務場景時,咱們深入的感覺到task-based工具的粗糙,團隊天天疲於根據各類業務場景編寫各類打包插件,構建邏輯異常複雜,隱隱看到不可控的跡象。

咱們很快意識到把基礎架構放到構建工具中實現是一件很愚蠢的事,試圖依靠構建工具實現各類優化策略使得構建變成了一個巨大的黑盒,一旦發生問題,定位起來很是困難,並且每種業務場景都有不一樣的優化需求,構建工具只能經過靜態分析來優化加載,具備很大的侷限性,單頁面/多頁面/PC端/移動端/前端渲染/後端渲染/多語言/多皮膚/高級優化等等資源加載問題,總不能給每一個都寫一套工具吧,更況且這些問題彼此之間還能夠有多種組合應用,工具根本寫不過來。

Facebook的作法無疑爲咱們亮起了一盞明燈,不過惋惜它並不開源(不是技術封鎖,而是這個系統依賴FB體系中的其餘方面,通用性不強,開源意義不大),咱們只能嘗試挖掘相關信息,網上對它的完整介紹仍是很是很是少,分析facebook的前端代碼也沒有太多收穫,後來無心中發現了facebook使用的項目管理工具phabricator中的一個靜態管理方案Celerity,以及相關的說明,看它的描述很像是Facebook靜態資源管理系統的一個mini版!

簡單看過整個系統以後發現原理並不複雜(小而美的典範),它是經過一個小工具掃描全部靜態資源,生成一張資源表,而後有一個PHP實現的資源管理框架(Celerity)提供了資源加載接口,替代了傳統的script/link等靜態的資源加載標籤,最終經過查表來加載資源。

雖然沒有真正看過FB的那套系統,但眼前的這個小小的框架給了當時的咱們足夠多的啓示:

靜態資源管理系統 = 資源表 + 資源加載框架

多麼優雅的實現啊!

資源表是一份數據文件(好比JSON),是項目中全部靜態資源(主要是JS和CSS)的構建信息記錄,經過構建工具掃描項目源碼生成,是一種k-v結構的數據,以每一個資源的id爲key,記錄了資源的類別、部署路徑、依賴關係、打包合併等內容,好比:

{ "a.js": { "url": "/static/js/a.5f100fa.js", "dep": [ "b.js", "a.css" ] }, "a.css": { "url": "/static/css/a.63cf374.css", "dep": [ "button.css" ] }, "b.js": { "url": "/static/js/b.97193bf.js" }, "button.css": { "url": "/static/css/button.de33108.js" } }

而資源加載框架則提供一些資源引用的API,讓開發者根據id來引用資源,替代靜態的script/link標籤來收集、去重、按需加載資源。調用這些接口時,框架經過查表來查找資源的各項信息,並遞歸查找其依賴的資源的信息,而後咱們能夠在這個過程當中實現各類性能優化算法來「智能」加載資源。

根據業務場景的不一樣,加載框架能夠在瀏覽器中用JS實現,也能夠是後端模板引擎中用服務端語言實現,甚至兩者的組合,不一而足。

有關加載框架的具體實現我曾寫過不少文章介紹,能夠擴展閱讀:

這種設計很快被驗證具備足夠的靈活性,可以完美支撐不一樣團隊不一樣技術規範下的性能優化需求,前面提到的按需加載、延遲加載、預加載、請求合併、文件指紋、CDN部署、Bigpipe、Quickling、BigRender、首屏CSS內嵌、HTTP 2.0服務端推送等等性能優化手段均可以很容易的在這種架構上實現,甚至能夠根據性能日誌自動進行優化(Facebook已實現)。

由於有了資源表,咱們能夠很方便的控制資源加載,經過各類手段在運行時計算頁面的資源使用狀況,從而得到最佳加載性能。不管是前端渲染的單頁面應用,仍是後端渲染的多頁面應用,這種方法都一樣適用。

此外,它還很巧妙的約束了構建工具的職責——只生成資源表。資源表是很是通用的數據結構,不管什麼業務場景,其業務代碼最終均可以被掃描爲相同結構的表數據,並標記資源間的依賴關係,有了表以後咱們只需根據不一樣的業務場景定製不一樣的資源加載框架就好了,今後完全告別一個團隊維護一套工具的時代!!!

恩,如你所見,雖然完全告別了一個團隊一套工具的時代,但彷佛又進入了一個團隊一套框架的時代。其實仍是有差異的,由於框架具備很大的靈活性,並且不那麼黑盒,採用框架實現資源管理相比構建更容易調試、定位和升級變動。

深耕靜態資源加載框架能夠帶來許多收益,並且有足夠的靈活性和健壯性面向將來的技術變革,這個咱們留做後話。

總結

回顧一下前面提到過的前端工程三個階段:

  • 第一階段:庫/框架選型
  • 第二階段:簡單構建優化
  • 第三階段:JS/CSS模塊化開發

如今補充上第四階段:

  • 第四階段:組件化開發與資源管理

因爲先天缺陷,前端相比其餘軟件開發,在基礎架構上更加迫切的須要組件化開發和資源管理,而解決資源管理的方法其實一點也不復雜:

一個通用的資源表生成工具 + 基於表的資源加載框架

近幾年來各類你聽到過的各類資源加載優化策略大部分均可以在這樣一套基礎上實現,而這種優化對於業務來講是徹底透明的,不須要重構的性能優化——這不正是咱們一直所期盼的嗎?正如魏小亮博士所說:咱們能夠把優秀的人集中起來去優化加載。

如何選型技術、如何定製規範、如何分治系統、如何優化性能、如何加載資源,當你從切圖開始轉變爲思考這些問題的時候,我想說:

你好,工程師!

相關文章
相關標籤/搜索