隨着互聯網的發展,咱們的業務也日益變得更加複雜且多樣化起來,前端工程師也再也不只是作簡單的頁面開發這麼簡單,咱們須要面對的十分複雜的系統性問題,例如,業務越來越複雜,咱們要如何清晰地梳理;團隊人員越來越多,咱們要如何更好地進行團隊協做;功能越來越多,咱們要如何保證頁面的性能不至於降低,等等。全部的這些均可以歸結爲如何提高開發體驗和性能問題。html

提高開發體驗

咱們主要從如下三個方面來提高咱們的開發體驗。前端

規範化

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

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

項目架構

組件化

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

組件化

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

組件化的好處主要有這麼幾點前端工程化

  • 管理方便,咱們能夠把一個獨立功能相關的文件在工程目錄中放在一塊兒,這樣代碼管理起來會很是便利
  • 組件複用,經過抽取公共組件,能夠實現組件複用,從而減小工做量,創造價值
  • 分而治之,這是組件化最重要的一點,將頁面組件化,就是對頁面功能的拆分,將一個大的工程拆成小的零件,咱們只須要關注每個零件的功能,極大地下降了頁面的開發與維護的難度

自動化編譯

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

曾經有大神說過,超過90s的工做都應該自動化掉。而以上全部的這些工做,貫穿咱們整個開發流程,可是不一樣工具的切換不但顯得凌亂,並且影響開發效率。在自動化、工程編譯的思想早已深刻人心的當下,咱們固然也要緊跟潮流,因此咱們考慮經過自動化手段來提高咱們的效率,讓全部操做能夠一鍵式開速執行完。緩存

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

提高性能

咱們主要從如下四個方面來作好性能優化。

首屏優化

頁面的打開速度一直是你們很是關心的一個指標,一個頁面打開太慢會讓讓用戶失去等待的耐心,爲了讓用戶更快地看到頁面,咱們考慮將頁面中部分靜態資源代碼直接嵌入頁面中,咱們經過工具處理,在工程編譯階段,將指定的靜態資源代碼內嵌入頁面中,這樣能夠減小HTTP請求,提高首屏加載速度,同時下降頁面裸奔風險。

按需加載

同時,咱們考慮經過儘可能減少頁面體積來提高頁面打開速度,在業務上咱們將頁面劃分爲一個個樓層組件,以京東美妝館爲例,頁面中從上而下分爲首焦、至IN尖貨、今日特惠、潮流前沿、口碑榜單這麼幾個樓層組件,其實這個頁面還有很長,內容很是多且複雜。

京東美妝館

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

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

按需加載1

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

按需加載2

而且給樓層容器打上標記

按需加載3

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

按需加載4

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

基於資源表加載

根據頁面組件化,經過工具分析,咱們將得到頁面與組件的依賴關係表,同時也能確認頁面所引用資源的依賴關係,例如,咱們在頁面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重命名後之間的對應關係,當咱們引用一個資源時,就會經過查表獲取重命名後的資源名,而後利用代碼中引用資源定位的能力來進行資源名自動替換。

md5

靜態資源預加載

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

靜態資源預加載設計

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

靜態資源預加載

Athena

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

Athena是由京東【凹凸實驗室】(aotu.io) 推出的一套項目流程工具,經過Athena,咱們能夠很流程地跑完整個開發流程。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功能,咱們能夠在預覽時任意修改文件,都將實時地反映到頁面中,同時能夠新建另外一個窗口執行新增組件和頁面的操做,讓整個開發過程很是順暢,咱們只需關注開發自己就好,不須要再關注其餘事。

ath serve

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

站點地圖

Mock server

在進行項目預覽的同時,Athena同時提供了mock data的服務,咱們能夠配置相應的路由,以及路由接口對應的假數據,全部的接口請求會發送到mock server上,在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 的方式,但後來因爲安裝、更新很是麻煩,命令太長很難打的緣由,咱們改爲了本身開發一個全局安裝包的方式,編譯核心使用的仍是 Gulp 的 vinyl-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指令將組件提交到組件平臺,組件平臺會對組件源碼進行編譯,將組件名稱md五、組件歸類以及組件版本記錄等等。

組件發佈

從組件平臺上下載組件

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

組件下載

權限控制

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

項目管理

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

項目管理

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

技術選型

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

技術選型

假數據服務

存在的問題

在日常的開發中,常常須要先後端聯調,可是在項目開始之初,不少接口並無提供,在之前的開發模式下,須要等待後端提供接口或者本身先定義接口,前端開發的進度可能會受影響。

Mock數據平臺

爲了避免影響前端開發的進度,咱們搭建了Mock數據平臺,經過與後端協商數據格式,自定義數據接口,這樣子就能夠作到先後端分離,讓前端獨立於後端進行開發。

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

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

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

Mock數據平臺

寫在最後

本次分享首先講述了咱們在業務膨脹、人員不斷增長的背景下遇到的項目開發上的問題,並提出了咱們本身對於這些問題思考總結後得出的解決方案與思路,最後產出適合咱們團隊、業務的開發工具—— Athena。但願咱們的方案能給你們帶來必定的借鑑做用。

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