1. 程式人生 > 其它 >【折騰不止】前端工程與效能優化

【折騰不止】前端工程與效能優化

作者:addy(許斌),前端開發工程師,文藝青年一枚,寫得了文章,編得了程式碼。

作為開發,不僅僅是前端,優化總是你繞不開的事,我們的目標就是要產品變得更快。優化的物件不僅僅指產品本身,還有我們日常的開發流程。從加入團隊到現在,大概有一半左右的時間花在了優化上,期間遇到了很多問題,回過頭看,優化這件事情並不是那麼簡單,也是些很瑣碎的事情,要打造一個無痛的開發流程,並且能夠相對輕鬆的對產品持續優化真的不輕鬆。沒有銀彈,任何專案都要花很多時間和精力。但任何值得改善的地方能夠有所提升都讓我們覺得努力沒白費。有時候只想懶一點,從枯燥的重複中解放出來。

剛進來時,加上我團隊中一共3個人專職前端開發。專案比較簡單,前人已經做好了模組化,只是覺得還不夠徹底。後來專案慢慢變得龐大,加上重構同學,人員最多的時候達到了13人。這個過程中遇到了很多問題,前端框架演變了三次,都是因為遇到了一些棘手的問題,而不得已做出調整,沒有絕對的好壞,只有合不合適。

1

1.0時代

前期模組化已經做的不錯了,至少不必花大量時間去重構程式碼。模組劃分如下圖,邏輯層次上還是比較清晰。

前端模組化依賴的主流庫也就數國內的Seajs和國外的requirejs,這裡就不陳述。採用了Seajs作為模組管理器,zepto作為基礎庫檔案,lib主要包含了專案中用到的主流第三方庫檔案。

我們知道模組化帶來的最大弊端便是HTTP請求數增加,所以上線的時候必須合併檔案。下圖中的package模組是檔案大集合,打包了很多個JS模組,除去上圖中的基礎庫檔案和業務模組層,在上線的時候大部分檔案都被打包在package.js裡。

大部分頁面的JS請求是這樣的:

細心點的同學可能注意到兩個問題:檔案的大小和載入時間。剛才的截圖還是在PC端擷取的,手機和不同網路環境的表現會更加糟糕。

現在來看下目錄

存在的問題:

  • 目錄看起來算規範,但實際上是公共的和業務的混在一塊。
  • 大部分檔案合併在一個檔案,合併策略不合理。
  • 由第二點引發的第三個問題,釋出上線時,只要兩人釋出涉及到package檔案,衝突必然發生。
  • 釋出時需要down下上一次的檔案,對照合併的新檔案,以免發錯。
  • 注意,第四點是人工。一不小心發錯,或者把他人剛釋出的檔案覆蓋了,這種事情發生10+次。
  • 只有一臺測試機器,測試環境經常覆蓋是常事。
  • 版本控制問題,不以SVN為版本,而是預釋出機器上程式碼,管理混亂

不敢想象如果10+人的團隊一起在這種模式下開發,會是怎樣的場面。

2

2.0時代

由第一個版本引起的問題,著實讓人很蛋疼,每次開發版本就是一次陣痛,尤其是測試、釋出環節。所以就開始慢慢著手解決。隨著業務擴充套件,人員增多,就誕生了下面這個圖。

優化措施:

  • 調整模組,讓共用的模組更加共用,業務模組跟隨業務自身。
  • 更改模組合併策略,既然大了,我就分成小,一定程度緩解了衝突。
  • 替換原有的同步檔案工具,包括測試與正式環境,接入ARS,提測釋出流程順暢多了。
  • ARS帶有衝突檢測功能,告別人工對照合併,覆蓋不再容易發生。
  • 公共JS檔案快取在localstorage中,模擬manifest,帶版本號控制。
  • 以SVN為板塊控制工具,不再對照外網程式碼。

一些統計

  • localstorage本地快取
  • localstorage快取命中率
  • 首屏時間
  • window.onload時間

一切看起來很美好,但是好景不長,因為新的問題又來了。

  1. 之前拆分package.js檔案為多個檔案,實際請求的時候則是合併了請求,包括JS檔案和CSS檔案。combo檔案實際上會有延遲問題,在釋出的時間節點上存在不同步的問題,直接導致頁面掛了。
  2. 拋開combo檔案不說,由於瀏覽器快取的問題,每次更新版本的時候要手動加上一個時間戳,來規避快取造成的錯誤。也是個很蛋疼的點。
  3. 隨著業務增長,越來越多頁面是放在APP裡面訪問。觸屏頁面已經不再是重點,如何更好的利用APP加速頁面才是關注點。很多人想到了手Q的離線包,但聽說實踐起來也不是特別方便,我們就採用了客戶端快取hash檔案的策略,告別304。所以這裡又涉及到自動化。
  4. 雪碧圖基本是手工;
  5. 程式碼混淆沒有壓縮;
  6. CSS合併檔案要手寫地址,類似下面:
http://at.qq.com/min/f=cssv4/common/reset.css,cssv4/common/base.css,cssv4/module/btns.css,cssv4/module/tab.css,cssv4/module/app-list.css,cssv4/module/talk-bar.css,cssv4/module/popup.css,cssv4/page/game-detail.css,cssv4/page/talk.css,cssv4/module/comments-bar.css

3

3.0時代

為了解決上述問題,流程需要進一步優化,簡單點就是讓自動化程度更提高。

3.1 探索期

前期在方案選擇上也做過一些討論,自己完全從底層寫時間上不允許。之前折騰過Grunt發現並不是那麼好用,後來發現百度的前端解決方案FIS能夠滿足我們的需求。

  1. 生成以hash值(字尾)命名的檔案,程式碼更改,生成新檔案,且都會自動更新HTML中的引用(核心訴求),就像下圖:
  1. 合併雪碧圖
  2. 壓縮混淆檔案
  3. 檔案合併(包括JS檔案和CSS檔案)

能做到這幾點基本就滿足了我們的需求。前期的一切都是未知的,不太明白會遇到什麼大問題。乍看起來非常好用,如果簡單的頁面,確實會很簡單,只要簡單幾行配置就可以搞定,但到現在FIS的配置檔案200+行。一些特性很難滿足,需要二次開發。上手簡單,要深入難,必須要看原始碼改原始碼,寫外掛,這大概就是用FIS的心得。

前期想了要怎樣把開發——測試——預釋出——釋出這個流程依賴工具流暢的跑起來,大概構思如下:

注:

  1. 除錯、釋出程式碼與原始碼分離
  2. 本地除錯用代理如fiddler,或者上開發機
  3. deploy是構建工具同步檔案的一個功能
  4. 保證原始碼的版本最新,釋出程式碼走ARS。

工程化進展卻不是想象中的順利,實踐中遇到了一些問題,也只能硬著頭皮咬著牙去解決。

3.2 煎熬期

  • 衝突問題 衝突問題一直存在,在2.0時代不那麼明顯罷了。原因是測試環境的JS已經被合併過一次。
  • 時間問題 由於剛開始檔案比較少,構建速度基本沒啥問題。後來業務越來越多,參與的開發也越來約多。檔案暴增到4000+,構建時間一步步增加。 開發除錯耗時 3987ms
  • 釋出構建的時候因為要進行md5計算,檔案壓縮等,要181745ms!已經無法忍了。
  • ARS流程 用過ARS的同學肯定明白,流程還是比較蛋疼,要完成提交SVN,點選同步等等一系列操作,繁瑣。
  • 構建命令 命令比較長,引數多,難以記憶
  • 產出檔案多,釋出麻煩 每改動一個字母,釋出的時候就會生成一個新的檔案,釋出的時候真是在檔案堆裡找!!

這一系列的問題如山倒,組內的開發同學已經沒法愉快的開發了。

3.3 深度優化

  • 減少衝突問題 進一步解決的方案還是細分模組,測試環境不進行檔案合併,這樣衝突的概率幾乎很小,因為公共庫經過2.0的調整已經基本穩定。
  • 縮短時間 構建時間這麼長,這樣發展下去是不行的。花了一些時間研究FIS原始碼,發現FIS監聽的是整個專案檔案,每一次構建都要掃描全部檔案,這樣時間必然會隨著檔案增加而變長。然後進行深度改造,變得不那麼像FIS了。 進行一次又一次優化,改變構建策略。依賴構建:當某個檔案依賴另一個檔案時,另一個檔案才會被構建。假如a.html依賴了b.js ,在構建產出的檔案就只有這兩個檔案,其他檔案不會被構建,檔案數也減少,時間大大縮短。

開發構建 (單位ms )

釋出構建 (單位ms)

  • 構建命令優化 輸入命令麻煩,就用GUI介面,點點按鈕就行。這裡要感謝@koppthe@kolawang同學短時間內用node寫的GUI。
  • 產出檔案多 依賴構建之後,只會產出相關檔案,產出檔案大大減少,釋出難度減少很多。
  • ARS流程 修復bug的時候不用ARS同步,監聽檔案變化直接同步到測試環境。
  • 只需要8ms!!!。如果打開了同步按鈕,修改的檔案會立馬上傳到測試環境上,會不會有相互覆蓋的問題。組內每個人負責的模組都不同,而且公共模組已經基本穩定,很難出現這樣的問題,在實踐中很少發生這樣的事情,相反小夥伴覺得簡直獲得解脫,相比找檔案傳檔案這樣繁瑣的流程,這輕鬆了許多。
  • 釋出優化 構建工具會產出檔案列表,點選就能開啟資料夾,找到對應檔案;列表對應SVN路徑,直接貼到ARS就能提單。
  • 雪碧圖的優化 釋出的時候所有引用的CSS檔案會合併成一個,然後將引用的圖示合併為雪碧圖,有點粗暴。因為公共的CSS檔案的圖片單獨合併為雪碧圖會更加合理,公共的圖片變動頻率不會那麼高。開發一外掛,在CSS合併前雪碧圖一次,合併後再雪碧圖一次。

統計與優化

使用者網路型別(粗略)

unknown是無法統計到的,理論上wifi還是佔大部分。從其他四種類型看,wifi佔據絕大部分,2g使用者非常少。

PC VS Mobile

在JS下載和執行效率上,移動端明顯要低於PC端。

JS優化

之前APP內部的JS檔案都是通過seajs來下載檔案,後來發覺何不直接乾脆點直接寫<script>下載就好了,優化後下載執行時間下降顯著:

注:這裡統計的時間,包括了下載JS和執行JS的時間。

JS內嵌與外聯對比

在考慮優化的時候,我們一種方案便是將外聯的JS程式碼(僅業務程式碼)通過工具內嵌到頁面。現在來對比下效能: http://gqq.gtimg.com/static/mobile/js/v3/page/gift/list/inappand.a9a524eb.lc.js 檔案大小8.7kb,gzip壓縮後3.3kb。 內聯載入時間幾乎為0 0.0015793,外聯的下載時間:

下載時間不到200ms,但相對來說已經是很長了,才8.7kb。

我們知道,DOMContentLoaded事件的觸發基本意味著頁面已經渲染完成,JS已經執行(非同步的除外),已經達到可互動的狀態了,具體可參考這篇文章。下面看下內聯與外聯對DOMContentLoaded的影響:

藍色線條是外聯的JS,時間明顯要比內聯的高出一些,大概200ms。由此可見,將小量的JS檔案內聯到頁面,能夠提高速度。但大的JS檔案適不適合內聯,目前還沒有實驗。這裡需要在減少HTTP請求和利用快取之間把握一個平衡,很多時候優化準則是並不是那麼容易實施,因為可能自相矛盾,也可能和工程本身相矛盾。優化不是按照準則照本宣科的做,需要靈活變通。

優化措施 按照雅虎優化的14條準則,把還沒做到的都處理了。

  • 指令碼域名切換,去cookie。
  • 檔案合併
  • 利用瀏覽器快取,無限增大快取期max-age=15552000
  • 雪碧圖
  • 減少HTTP請求,構建工具內嵌JS到HTML

感覺速度還是不夠快,再充分利用本地快取和APP提供的快取能力

  • 瀏覽器使用localstorage快取指令碼
  • APP快取hash檔名指令碼
  • 快取HTML片段

除錯、測試、體驗流程

反向代理+白名單控制策略,域名對外訪問是403,公司內網可訪問。不用代理,手機直接連線wifi訪問。環境分為開發——測試——預釋出——正式(每個環境對應一個獨立域名),任何角色(開發、測試、設計、產品)都可隨時訪問。

APP的debug包,可任意切換上面四種環境進行除錯、測試、體驗。

總結

這是過去一年工作的總結,一直都沒靜下來梳理。回過頭想,自己是不是把這個流程變得更加複雜了。可能都有一個從簡單到複雜再到簡單的過程,堅持優化一直沒有停下來,只要能夠變得更好一點點,都會去嘗試,所謂生命不息,折騰不止。