淘寶新勢力周(春上新)是命運石kimi鏈路(H5鏈路)第一次承接S級大促,面對S級大促內容豐富且複雜的頁面需求,kimi鏈路遇到了一些性能問題,在未進行性能優化以前,搭建出來的頁面,業務方廣泛反饋頁面卡頓嚴重,沒法滑動。android
由於時髦女裝會場是反饋比較嚴重的頁面之一,因此我以時髦女裝會場爲例子,介紹下此次性能優化的具體方案。時髦女裝會場頁面模塊在18個左右,頁面上的img標籤數量在200左右,頁面總長度 31208px,以iPhone6頁面一屏736px爲標準,總共能分爲42.4屏左右。爲何我要特別把img標籤寫出來呢?由於此次的性能卡頓主要的緣由是由於錯誤使用圖片懶加載引發的。web
現代的web性能優化離不開chrome devtool裏performance的幫助,咱們先來看一張未優化以前 performance的截圖chrome
這張performance圖咱們主要看三個部分:第一個是最上面FPS紅線的部分,紅線表明着這段時間內未達到60FPS每幀;第二部分是Frames的耗時,勾選了Screenshots後咱們能看到每幀的耗時;第三部分是下面函數耗時,咱們能從函數耗時裏分析出來究竟是哪段代碼block住了頁面渲染,致使卡幀。緩存
從上面的圖能夠看到最長的一幀耗時3.37秒,這致使FPS都快接近0了。性能優化
把函數耗時圖拉大分析裏面耗時最長的函數,能夠看到耗時最長的函數是inview函數,這個函數是圖片懶加載庫裏面檢查當前圖片是否在屏幕中間的函數。多線程
圖片懶加載庫的基本邏輯是:當調用初始化函數時當即檢查當前頁面上全部未真正加載的圖片,判斷是否須要加載。當頁面進行滑動時,重複檢查全部圖片的邏輯。異步
卡頓掉幀的緣由:此次搭建出來的頁面使用的是外包同窗開發的業務模塊,在模塊內部手動調用了lazyLoad初始函數,因此每初始化一個模塊就會當即檢查全部未加載圖片,當頁面上圖片數量不斷增加的時候,inview函數的耗時也不斷增長,檢查一個圖片是否在頁面的耗時是2ms~5ms,若是頁面中有100個圖片未加載當頁面滑動時每一次檢查會耗時200ms~500ms,若是檢查是同步操做的話,掉幀幾乎沒法避免。函數
優化方案:以前的其餘鏈路的優化方案是模塊懶加載,而後lazyload統一調用,可是由於此次離上線時間較緊張,讓外包返工改模塊風險較大,因而有另外的一個優化方案:圖片懶加載庫的異步化,只要避免函數執行耗時過長阻塞渲染,就能避免卡幀,假設咱們有100張圖片,咱們分多批次進行檢查,避免一次檢查全部圖片阻塞渲染。另外針對模塊初始化時頻繁的檢查全部圖片的問題,咱們給這段邏輯加上debounce函數和圖片緩存隊列。性能
在我接手以前,有一版優化是將模塊的渲染經過setTimeout函數改爲異步的;這個優化是幾乎沒有效果的,優化後頁面依然卡頓掉幀,由於這個優化並無找到頁面卡頓的緣由。起碼也應該將setTimeout改爲RAF。固然模塊的延時加載並不能解決卡頓問題,可是模塊的懶加載能解決一部分問題。下面咱們看一張使用模塊懶加載後的performance圖優化
模塊懶加載後,一長條紅色塊已經變成了短條的紅色塊,可是由於模塊內部單獨使用圖片懶加載致使頻繁檢查全部圖片是否在可視範圍內的問題仍是沒有獲得解決,最長的一幀達到855ms,依然存在掉幀。
圖片懶加載異步版本:經過對圖片懶加載庫的改造,一、初始化時加上debound優化和圖片緩存隊列,二、分批檢查圖片。咱們在看一下優化後的performance圖
紅色的條塊也消失,看下面函數執行變的又長有尖,這是由於檢查圖片的操做變成異步分批了。
在將圖片懶加載改形成異步的時候遇到了一個問題,就和Java多線程同樣,不少時候異步咱們也但願是有序的異步。
分批檢查的有序是比較容易保證的,將圖片分紅多批,一批一批進行,再最後一批結束任務。可是問題出在分批檢查和圖片懶加載模塊初始化存在交替運行的狀況,而這兩個任務都會改變一個變量。若是不能解決這個問題,就會出現圖片有時候能正常加載,有時候加載不出來的狀況。因此有說法是,大部分偶現的問題都是異步並行的問題。
解決這個問題的思路也比較常見,就是經過鎖,當一個操做異步變量的任務開啓,咱們的鎖自增1,完成異步任務時自減,圖片懶加載庫的圖片緩存初始隊列等到異步鎖釋放後再進行檢查,不然存入緩存隊列,等待下一幀再檢查。
優化事後,對應常見的機型基本能保證頁面流暢不卡頓。chrome的performance圖基本上和真機操做的狀況保持一致,若是performance出現掉幀,那iPhone6s上和android上基本也會出現掉幀,可是iPhone7以上的機器卻可能感覺不明顯。經過performance可以快速定位掉幀的問題,經過解決這些問題實質性的優化頁面性能,而不是經過猜想進行無效優化。