1. 程式人生 > >[轉載]我們是如何做好前端工程化和靜態資源管理

[轉載]我們是如何做好前端工程化和靜態資源管理

xpl 剔除 UNC 經驗 編譯時間 的人 前端 傳遞 ons

技術分享圖片

隨著互聯網的發展,我們的業務也日益變得更加復雜且多樣化起來,前端工程師也不再只是做簡單的頁面開發這麽簡單,我們需要面對的十分復雜的系統性問題,例如,業務愈來愈復雜,我們要如何清晰地梳理;團隊人員愈來愈多,我們要如何更好地進行團隊協作;功能愈來愈多,我們要如何保證頁面的性能不至於下降,等等。所有的這些都可以歸結為如何提升開發體驗和性能問題。

提升開發體驗

我們主要從以下三個方面來提升我們的開發體驗。

規範化

當團隊人員不斷擴充時,我們需要制定統一的規範來對平時的開發工作做出一定約束和指導。統一的規範包括前端的代碼規範,根據規範定義好一套代碼檢查的規則,在代碼提交的時候進行檢查,讓開發人員知道自己的代碼情況。

同時,根據以往的開發經驗,我們制定了統一的項目框架,根據業務功能不同,將一個項目(app)拆分成不同的業務模塊(module),而每一個模塊都包含自身的頁面(page)以及構成頁面所需要的組件(widget),每一個項目涉及到app、module、page、widget這些已經約定好的概念,這樣讓項目結構更加清晰,而且讓團隊內不同業務的人員之間切換無障礙。

技術分享圖片

組件化

在項目中引入組件化的概念,這裏的組件對應上文講到的widget,每一個組件都會包含組件自身的模板、css、js、圖片以及說明文件,我們使用組件來拼裝頁面,像搭積木一樣來拼裝我們的頁面,同時一個組件內可以調用另一個組件。

技術分享圖片

在拿到設計稿後,我們首先需要確定哪些需要做成公共組件,那些是要做成獨立組件,以及組件間如何進行通信。在頁面中調用這些組件後,會自動加載組件的模板以及組件的靜態資源,而當組件不再需要時,只要移除掉組件引用,那麽相應的模板和靜態資源也會不再加載。

組件化的好處主要有這麽幾點

  • 管理方便,我們可以把一個獨立功能相關的文件在工程目錄中放在一起,這樣代碼管理起來會非常便利
  • 組件復用,通過抽取公共組件,可以實現組件復用,從而減少工作量,創造價值
  • 分而治之,這是組件化最重要的一點,將頁面組件化,就是對頁面功能的拆分,將一個大的工程拆成小的零件,我們只需要關註每一個零件的功能,極大地降低了頁面的開發與維護的難度

自動化編譯

在前端開發中,我們總是會去使用很多工具、手段來優化代碼、提升開發效率,例如,我們會使用sass、less等CSS預處理工具來編寫更好維護的樣式代碼,我們也會使用CSSLint、eslint等代碼檢查工具來檢查代碼的語法錯誤,使用文件合並壓縮等手段來減少資源大小,除此之外我們還會去做雪碧圖合並、多倍圖處理、字體壓縮處理、代碼發布等等。

曾經有大神說過,超過90s的工作都應該自動化掉。而以上所有的這些工作,貫穿我們整個開發流程,但是不同工具的切換不但顯得淩亂,而且影響開發效率。在自動化、工程編譯的思想早已深入人心的當下,我們當然也要緊跟潮流,所以我們考慮通過自動化手段來提升我們的效率,讓所有操作可以一鍵式開速執行完。

我們將通過定義好一系列的編譯任務,按照一定順序依次對我們的項目自動進行編譯操作,最後產生出可上線的代碼。

提升性能

我們主要從以下四個方面來做好性能優化。

首屏優化

頁面的打開速度一直是大家非常關心的一個指標,一個頁面打開太慢會讓讓用戶失去等待的耐心,為了讓用戶更快地看到頁面,我們考慮將頁面中部分靜態資源代碼直接嵌入頁面中,我們通過工具處理,在工程編譯階段,將指定的靜態資源代碼內嵌入頁面中,這樣可以減少HTTP請求,提升首屏加載速度,同時降低頁面裸奔風險。

按需加載

同時,我們考慮通過盡量減小頁面體積來提升頁面打開速度,在業務上我們將頁面劃分為一個個樓層組件,以京東美妝館為例,頁面中從上而下分為首焦、至IN尖貨、今日特惠、潮流前沿、口碑榜單這麽幾個樓層組件,其實這個頁面還有很長,內容非常多且復雜。

技術分享圖片

之前我們的做法是整個頁面直出,這樣一次性加載的內容會非常多,為了提升打開速度,我們考慮通過按需加載的方式來優化頁面的加載。我們在頁面中只放每一個樓層的框架性代碼,樓層的模板和數據都通過異步的方式去拉取,來實現樓層組件的按需加載,同時我們可以對模板以及數據進行緩存,以此來減少請求,做更極致的優化。在開發中我們以正常組件的方式去開發整個頁面,隨後通過編譯工具,在代碼編譯階段自動將樓層的模板抽離成一個獨立的JS文件,並給樓層容器打上標記位,通過頁面加載邏輯去按需拉取模板,再進行渲染。

通過給樓層容器和模板分別加上標記位 o2-out-tpl-wrapper o2-out-tpl

技術分享圖片

在編譯時自動將指定的模板代碼抽離成獨立js文件

技術分享圖片

並且給樓層容器打上標記

技術分享圖片

同時在邏輯腳本適當位置自動加入模板的版本

技術分享圖片

通過上述步驟,實現按需加載的自動化生成,在提升性能的同時,很好地解放我們生產力。

基於資源表加載

根據頁面組件化,通過工具分析,我們將獲得頁面與組件的依賴關系表,同時也能確認頁面所引用資源的依賴關系,例如,我們在頁面hello中同步引用組件topbar,那麽依賴關系表中將會記錄同步引用關系hello引用topbar.tpl、topbar.css、topbar.js,那麽頁面hello將會自動加載組件topbar的CSS與JS,同時依賴表會記錄異步引用的關系,假如我們在組件C中通過API異步引用了組件D的js,那麽會在依賴表中記錄C異步引用D.js這一個依賴關系,這樣D.js這個資源將會在用到的時候被異步調用。

技術分享圖片

技術分享圖片

同步引用的資源通過生成combo形式鏈接,在服務端進行文件合並,這樣在頁面加載的時候,頁面只會加載自己需要的同步資源,異步的資源將會在用到的時候再加載,有效避免資源冗余。同時刪除、增加組件也非常方便,只需改動模板中對組件調用,通過編譯工具會自動重新生成模板以及combo鏈接。

我們可以將資源加載的操作抽離出來,形成一套統一的資源加載框架設計,這樣我們使用的模板可以變得更加靈活,無論是純html模板,還是PHP或Java之類的後端模板都能有效支持。編譯工具掃描代碼後只生成資源依賴表,我們通過實現各語言平臺的資源加載框架,讓不同語言的模板都能基於同一個資源依賴表進行資源加載。

同時,對資源進行MD5重命名處理,文件md5重命名也是一種提升性能的有效手段,使用文件md5後開啟服務器強緩存,可以提升緩存的利用率並避免不必要的緩存判斷處理。但文件md5重命名後會出現開發時引用的文件名對不上的問題,這就需要在資源表中記錄原文件名與md5重命名後之間的對應關系,當我們引用一個資源時,就會通過查表獲取重命名後的資源名,然後利用代碼中引用資源定位的能力來進行資源名自動替換。

技術分享圖片

靜態資源預加載

所謂靜態資源預加載,就是當用戶在進行瀏覽頁面的時候,我們可以在當前頁面靜默加載下一個頁面的靜態資源,這樣當用戶進入到下一個頁面時就能快速打開頁面,從而在不知不覺中提升頁面的打開速度。

技術分享圖片

我們會在靜態資源預加載平臺上配置每一個頁面id對應需要預加載頁面資源的id,然後系統通過讀取資源依賴表獲取到所需要預加載的靜態資源,生成預加載資源列表文件,再將文件推送到線上服務器,通過頁面掛載js請求獲取預加載資源列表,隨後靜默加載資源。在有了資源依賴表後,我們可以準確地分析到每一個頁面引用資源的請求,就可以很好地實現靜態資源預加載的功能。

技術分享圖片

Athena

工欲善其事,必現利其器。為了實現我們對提升開發效率和產品性能的訴求,我們提出了比較完整的工程化解決方案以及對應的工具Athena。

Athena是由京東【凹凸實驗室】(aotu.io) 推出的一套項目流程工具,通過Athena,我們可以很流程地跑完整個開發流程。Athena分為兩部分,一是本地自動化編譯工具,二是資源管理平臺,其架構如下

技術分享圖片

本地自動化工具

Athena本地編譯工具是一個基於NodeJs的命令行工具,通過執行命令的方式來優化我們的開發流程,目前Athena的主要功能有

  • 自動創建項目、模塊、頁面、組件結構
  • 輕量組件化功能,根據組件加載情況生成資源依賴表
  • Sass/less 編譯
  • 代碼檢查
  • CSS prefix等處理
  • CSS合並壓縮,JS合並壓縮
  • 自動生成雪碧圖,自動多倍圖,圖片壓縮
  • 字體文件壓縮
  • 自定義圖片轉base64
  • 文件內聯,可以內聯樣式及JS代碼
  • 文件MD5戳,將文件進行使用MD5進行重命名
  • 本地預覽,直接查看整個項目
  • 資源定位(圖片等資源路徑替換)
  • 生成CSS頁面片,提供將頁面引用的CSS/JS抽離成頁面片的形式,方便管理CSS資源
  • 部署到預覽機和開發機

創建項目結構

在執行創建命令時,Athena會從管理平臺下載自定義好的項目模板,可以根據模板創建項目、模塊、頁面、和組件。Athena有四個創建命令:

通過執行 $ ath app demo 命令就可以生成定義好目錄結構的項目。

技術分享圖片

隨後可以通過 $ ath module home來創建一個業務模塊;

通過 $ ath page index 來創建頁面;

通過 $ ath widget widgetName 來創建組件。

開發使用

組件化

Athena中實現組件化主要是分為兩種,一是針對純HTML模板,通過擴展模板引擎方法實現,提供了組件化API widget.load,它可以方法接收三個參數,第一個參數是widget的名稱,後面兩個參數是可選參數,第二個是向widget傳遞的一些參數,第三個是widget所屬的模塊,如果是本模塊,可以不傳例如

1 2 3 4 5 6 7 <%= widget.load(‘user‘) %> <%= widget.load(‘user‘, { param: ‘test‘ }) %> <%= widget.load(‘user‘, null, ‘gb‘) %>

通過模板引擎編譯,執行widget.load方法,可以實現加載模板,記錄依賴關系的目的。

技術分享圖片

二是針對不同語言的後端模板,通過實現各自的組件化框架來進行組件的加載,例如 PHP 下使用 <?= $widget->load(‘user‘, NULL, ‘gb‘) ?> 來進行組件加載,再通過代碼掃描得出組件依賴關系。

Athena中的API

Athena針對模板提供了一系列的API來擴展豐富的功能,例如前面提到的 <%= widget.load() %> 來實現組件化。

同時Athena中還提供了其他API:

<%= getCSS() %><%= getJS() %> 用來引用CSS/JS文件,傳入文件名和模塊名;

<%= uri() %> 提供了資源定位功能,可以在模板中標記資源,編譯過程中會進行替換,而且在JS中也有資源定位API __uri()

<%= inline() %> 提供了內聯資源的功能,傳入文件名和模塊名,可以在模板中內聯任意資源,例如圖片以及JS腳本;而且 inline 也可以內聯一段網絡資源,例如線上的JS文件,同樣的在JS中也有內聯資源API __inline()

雪碧圖標識 ?__sprite ,在CSS中引用圖片最後加上標識 ?__sprite 可以自動生成自定義名稱雪碧圖,同時支持自定義生成多張雪碧圖,只需要要標識後面帶上一個文件名,就可以生成一張以這個文件名來命名的雪碧圖,例如 ?__sprite=icons ,這樣所有帶同樣標識的圖片就會生成一張以 icons 為文件名的雪碧圖。

編譯預覽

編譯任務

在編寫完項目,就可以通過命令來對項目進行編譯了,執行編譯命令 $ ath build,會針對指定模塊執行已經定義好的編譯任務,根據項目需求,目前編譯都是基於業務模塊去編譯,編譯任務的最小執行單位是頁面,每次編譯都會執行以下編譯列表

技術分享圖片

技術分享圖片

本地預覽

執行預覽命令 $ath serve 會執行精簡版編譯任務來編譯項目,編譯完項目後會生成一份站點地圖,隨後打開一個本地服務器來預覽項目,使用這個命令可以很方便地進行開發,在預覽時會同時watch目錄和文件的改動,並且提供了livereload功能,我們可以在預覽時任意修改文件,都將實時地反映到頁面中,同時可以新建另一個窗口執行新增組件和頁面的操作,讓整個開發過程非常順暢,我們只需關註開發本身就好,不需要再關註其他事。

技術分享圖片

執行完編譯任務後,默認自動打開瀏覽器,預覽站點地圖

技術分享圖片

Mock server

在進行項目預覽的同時,Athena同時提供了mock data的服務,我們可以配置相應的路由,以及路由接口對應的假數據,所有的接口請求會發送到mock server上,在mock server中可以選擇將請求代理到假數據平臺還是代理到線上接口,這樣就可以脫離後端進行開發聯調了,以此實現數據的前後端分離。

技術分享圖片

項目部署

在開發預覽完後,通過命令 $ ath publish 就可以將項目發布到配置好的測試機上,發布同時支持ftp、sftp以及http形式。

組件維護

我們通過組件化的手段已經將我們的項目進行組件化了,這樣我們經過業務叠代積累,產出很多業務公共組件,但在以往的項目開發中,公共組件的更新與維護一直很受限制,而且有哪些公共組件、公共組件長什麽樣子,只能依靠口口相傳或者手工維護的文檔。所以在Athena中我們加入了組件平臺,在組件平臺上統一展示各個業務的公共組件,而得益於本地工具,組件平臺不需要人工幹預維護,我們可以在本地通過命令 $ ath widget-publish [widgetName] 命令來發布一個組件到組件平臺,這樣其他人就可以立即在組件平臺進行組件的預覽,而其他人若想使用該組件時,在本地通過命令 ath widget-load [widgetId] 就可以下載該組件到自己的模塊目錄下了。

這樣組件的維護更加自動化,公共組件的使用也更加方便了。

組件發布

技術分享圖片

組件下載

技術分享圖片

自身優化

為了提升開發效率,Athena做了一些優化操作

精簡項目預覽時的任務

在開發時進行項目預覽時,會執行精簡版的編譯任務,剔除了類似文件壓縮、雪碧圖生成、模板抽離處理等耗時的操作,只保留核心、必須的編譯任務,這樣可以極大地減少編譯時間,提升開發的效率。

預覽時監聽細化

在開發進行預覽時,會對所有文件的改動進行監聽,而針對每一類文件都有非常細化的操作,當文件改動時只會執行改文件所需要的編譯任務,而不會進行整體編譯,這樣可以很好地提升開發效率。例如改動某一組件的CSS文件,則只會針對該文件執行一些相關的CSS操作。

同時得益於所有文件依賴關系的記錄,在監聽時會根據依賴關系進行文件編譯,例如某sass文件中引入了另一個sass庫文件,修改這個sass庫文件的時候,會根據引用關系表同時更新到所有引用到這個sass文件的文件,這樣項目文件更新及時,讓開發流程更加流暢。

編譯緩存

在圖片壓縮和sass編譯時,開啟文件緩存,將已經編譯過且沒有改動的文件過濾掉,不再編譯,大幅提升編譯速度。

發布緩存

設置發布過濾,根據文件md5過濾掉已經發布過的文件,提升發布速度。

技術選型

Athena本地工具早期技術選型是 Yeoman + Gulp 的方式,但後來由於安裝、更新非常麻煩,命令太長很難打的原因,我們改成了自己開發一個全局安裝包的方式,編譯核心使用的還是 Gulpvinyl-fs 來實現文件流處理,通過 ES6 Promise 來進行編譯流程控制,最小以頁面為單位,經過一系列編譯任務,最後產出編譯好的文件。

技術分享圖片

管理平臺

性能優化一直是前端工程師探索的課題,很多時候就是資源的分配問題,也就是資源管理。為了更好地配合本地構建工具來管理資源,我們搭建了管理平臺。我們來看下,結合本地構建工具和管理平臺,工作流程變成了怎樣?

工作流程

  1. 在管理平臺上創建項目,輸入項目名稱和預覽機,以及選擇相應的模板等;
  2. 在終端執行ath app指令,工具會優先拉取遠程服務器的項目信息來初始化項目,如果沒有獲取到相關信息,就會在本地生成項目,並將項目信息上報給服務器;
  3. 項目初始化後,就可以創建模塊、頁面、組件了;
  4. 在編碼過程中,可通過ath server預覽頁面;
  5. 在本地通過後,可執行ath publish將代碼發布到開發機或者預覽機。

在上面的publish指令中,工具會掃描所有文件,執行代碼檢查,掃描頁面文件,獲取組件依賴關系,根據組件依賴關系進行文件合並,然後會進行樣式處理、js處理以及圖片的處理,根據配置是否進行md5重命名文件,組裝html,插入樣式、js和圖片,最後將編譯好的文件發布到相應的機器。在整個過程裏面,會生成資源關系依賴表,最終會將資源關系表及編譯後的文件上傳至管理平臺。

除此之外,每個指令的操作都會上報給管理平臺。管理平臺收到數據後,會對數據進行處理,最終可以在平臺上看到項目相關的信息。

整體工作流程圖如下:

技術分享圖片

從上面的工作流程中,我們可以看到,管理平臺需要有數據統計、資源管理以及項目管理的功能。整體架構圖如下:

技術分享圖片

數據統計

數據統計包含項目操作日誌,主要是用於統計團隊每個成員具體的操作,方便項目成員查看項目代碼變更;另一部份是統計樣式表、腳本以及圖片的壓縮數據,用於顯示工具給我們項目帶來的提升。

以下是操作日誌統計:

技術分享圖片

資源管理

資源管理是管理平臺的核心,主要分為4個部分:模塊展示、依賴關系、組件預覽和權限控制。這部分功能主要通過本地構建工具提供的資源關系表來完成。

模塊展示

模塊展示,用於記錄項目具體包含哪些模塊以及模塊具體的信息。在平常開發中,我們的項目會分為許多模塊,不同的模塊有不同的人來開發和維護。當項目越大的時候,可以通過管理平臺清晰地看到模塊具體的信息。

技術分享圖片

依賴關系

依賴關系,主要是html、css、js和圖片相互之間的關系。通過分析資源關系依賴表,可以獲取到各個資源被引用的情況以及線上版本的情況。當線上環境采用md5來做資源管理時,我們不是很清晰地知道靜態資源對應線上哪個版本的資源,而有了這個依賴關系表,當出現問題時,我們可以更快地定位到具體的資源。

技術分享圖片

組件管理

我們采用組件來拼湊頁面,當項目越大時,組件越多,那麽如何管理組件成為了一個棘手的問題。比如說,有一些比較老的冗余組件,我們不確定是否為其他頁面所引用,那麽就不能愉快地刪除它。有了組件管理,可以清晰地知道組件的被調用情況,就可以對組件做相應的操作。

組件管理,結合組件平臺來使用,在管理平臺上引用組件地址預覽組件,同時可以獲取到組件被引用以及引用資源(如css、js、圖片)的相關情況。

技術分享圖片

我們的組件分為兩種,一類是通過ath w自動創建的,通過ath pu提交到管理平臺的,在管理平臺上進行組件的相關分析和編譯,得到組件的信息,這類組件主要是跟業務綁定的;另一類是通過ath widget-publish提交到組件平臺的,由組件平臺進行相關處理,這類組件是通用組件,與業務無關,用於展示給開發以及相關業務方看的。

技術分享圖片

在組件平臺上可以預覽與編輯相關的組件,通過與設計師約定相關的設計規範來促使組件達到盡可能地復用,進而減少設計師的工作量,提升我們的工作效率。

技術分享圖片

組件提交到組件平臺

通過ath widget-publish指令將組件提交到組件平臺,組件平臺會對組件源碼進行編譯,將組件名稱md5、組件歸類以及組件版本記錄等等。

技術分享圖片

從組件平臺上下載組件

通過ath widget-load指令將組件下載到本地,當本地構建工具向組件平臺發起請求時,會帶上組件名稱,組件平臺會將源碼進行編譯,將組件名稱重命名,並且相應地替換源碼中的組件名稱,同時記錄組件的被引用記錄。

技術分享圖片

權限控制

權限控制,項目中存在公共組件模塊,公共組件比較穩定,比如說輪播組件、選項卡組件等等,這部分代碼一般比較少變動,可由少部分人來更新和維護,所以加入了權限控制機制,保證公共組件的穩定性。

項目管理

我們在使用本地構建工具時,需要配置多個參數,比如主機信息、選擇模版等,在命令行環境下有些不直觀。為了簡化這個操作,管理平臺提供了項目創建的功能,同時提供了模版創建的功能。

技術分享圖片

在項目信息、模塊信息以及組件信息發生變更的時候,為了第一時間能夠通知項目成員更新,加入了消息通知的功能,目前通過發送郵件的方式,後期可以加入微信提醒的功能。

技術選型

管理平臺前端采用React+Redux的方式,後端采用Express+MongoDB,整體技術選型如下:

技術分享圖片

假數據服務

存在的問題

在平常的開發中,經常需要前後端聯調,但是在項目開始之初,很多接口並沒有提供,在以前的開發模式下,需要等待後端提供接口或者自己先定義接口,前端開發的進度可能會受影響。

Mock數據平臺

為了不影響前端開發的進度,我們搭建了Mock數據平臺,通過與後端協商數據格式,自定義數據接口,這樣子就可以做到前後端分離,讓前端獨立於後端進行開發。

Mock數據平臺基於mockjs搭建而成,通過簡單的mock語法來生成數據。

Mock數據平臺目前有如下功能:

  1. 創建模擬數據,使之符合各種場景;
  2. 生成json數據接口,支持CORS以及jsonp。

技術分享圖片

寫在最後

本次分享首先講述了我們在業務膨脹、人員不斷增加的背景下遇到的項目開發上的問題,並提出了我們自己對於這些問題思考總結後得出的解決方案與思路,最後產出適合我們團隊、業務的開發工具—— Athena。希望我們的方案能給大家帶來一定的借鑒作用。

感謝您的閱讀,本文由 凹凸實驗室 版權所有。如若轉載,請註明出處:凹凸實驗室(https://aotu.io/notes/2016/07/19/A-little-exploration-of-front-end-engineering/)

[轉載]我們是如何做好前端工程化和靜態資源管理