前端「智能」靜態資源管理

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

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

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

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

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

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

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

因此我以爲:後端

前端開發最迫切須要作好的就是在基礎架構中貫徹增量原則。瀏覽器

  相信這種貫徹不會隨着時間的推移而改變,在可預見的將來,不管在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已實現)。

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

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

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

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

  本文來自http://github.com/fouber/blog,著做權屬於原做者@前端農名工。

相關文章
相關標籤/搜索