歡迎關注個人我的公衆號,不按期更新本身的工做心得。css
題記:能夠把這篇文章當作一次事故記錄,一次線上事故。此刻我能坐在這裏寫下這篇文章,要感謝我司沒有開除我,或者說我解決了此次性能問題,因此我有機會寫下這篇文章。html
我司全球購業務改版,將原生的全球購頁面採用Hybird模式開發,這是我司H5頁面第一次出如今以及入口位置,做爲開發者顯得非常興奮。html5
根據設計稿,如期完成工做,測試也已準備好,部分Android低端機中顯示出卡頓現象,出於對自身技能的確定,誤覺得是硬件性能問題,並未引發過多關注。ios
未引發重視,並無作出相應處理。web
上到pre環境後,發現部分iPhone機型出現崩潰,初步排查在iOS 8.4系統上DOM節點高度達到10000px以上奔潰率100%,其餘8-9之間的系統版本當分頁數據使得DOM達到10000px以上快速滾動會出現卡頓,9以上iOS系統與Android4.4以上系統均表現良好。定位問題後,迅速進行更多特殊機型與相應系統的測試,期間借遍了公司幾百位小夥伴的手機,在此向他們表示感謝。chrome
你問我爲何,我只能說測試環境沒有這麼多數據,至於爲何沒有?呵呵,你懂得!瀏覽器
iOS將UIWebview切換爲WKWebview,WKWebview爲iOS在8以上系統中新加入的一個高性能Webview,具體哪裏好,能夠點這裏 這裏 還有這裏 ,因爲iOS已經發版(對的,在H5頁面還在pre環境測試的時候,iOS已經發版了)這個方案只能做爲下一個版本了性能優化
臨時下降數據顯示總量,同時尋找性能瓶頸(這裏要和運營的同窗說聲sorry)微信
針對iOS 8如下以及8.4系統 Android 4.4如下系統作自動降級處理。框架
測試不能僅僅作功能測試,還要作性能測試
開發人員常使用chrome、Firefox等瀏覽器的開發工具進行開發,這裏要注意的是:瀏覽器開發工具只能模擬手機UI與交互,其性能要遠遠強於手機環境。
開發過程當中經常使用微信、QQ、手機瀏覽器進行測試,這是不可取的,由於最終環境不一,容易給本身形成錯覺,以爲在微信 QQ的Webview中沒問題,就OK了
deadline 真的要好好定。不少人認爲H5很靈活,並不依賴於iOS發版,能夠將測試定在iOS發版的那幾天進行。其實這真的是巨大的錯誤,後面我會說明。
通過降級後,仍然存在不少問題,同時咱們也在抓緊研究後續升級方案。通過協調,將系統切回了老版本暫時使用原生替代H5(感受很受打擊~)。
數據量不足,對運營影響很大
對降級機型的體驗不好,數據統計顯示,受影響的機型仍是有必定份額
因爲這次H5是存在於以及目錄,因此設計上是常駐內存以保證體驗,UIWebview的內存一直得不到釋放,同事系統中其餘頁面也使用到了UIWebview,長時間使用仍然會出現崩潰。
距離iOS和Android下一次發版還有一段時間,不可能一直降級
不管如何解決性能問題,這是惟一目標!
通過一個星期枯燥而漫長的努力與實驗,從如下幾個方面完全解決了性能瓶頸;
iScroll是咱們內部框架中引入的一個第三方組件,官方的介紹是「Smooth scrolling for the web」,奈何在大數據量面前性能確實使人堪憂。若是數據量少,仍是推薦使用的。
在對比了競品,和天貓、JD等知名廠商H5實現後,確實鬱悶了一段時間,一樣的數據量爲何別人能夠作到?最後獲得兩個結論:
別人DOM節點沒咱們複雜,因爲咱們採用了內部研發的m-fast框架,框架頂層就肯定了DOM結構的複雜性,例如:框架預設了整個佈局爲上、下、左、右、中的佈局,即便個人頁面只是一個流式佈局。
別人H5頁面沒有圖片輪播!對,你沒看錯,一個圖片輪播竟然對性能影響如此之大
居於上面這兩點,我開始了DOM性能研究
m-fast框架採用模板預編譯技術,將頁面模板編譯爲js文件,在經過異步加載的方式載入,因此,網頁的渲染大體符合上面這五個步驟
JavaScript。通常來講,咱們會使用JavaScript來實現一些視覺變化的效果。好比用jQuery的animate
函數作一個動畫、對一個數據集進行排序、或者往頁面裏添加一些DOM元素等。固然,除了JavaScript,還有其餘一些經常使用方法也能夠實現視覺變化效果,好比:CSS Animations, Transitions和Web Animation API。
計算樣式。這個過程是根據CSS選擇器,好比.headline
或.nav > .nav_item
,對每一個DOM元素匹配對應的CSS樣式。這一步結束以後,就肯定了每一個DOM元素上該應用什麼CSS樣式規則。
佈局。上一步肯定了每一個DOM元素的樣式規則,這一步就是具體計算每一個DOM元素最終在屏幕上顯示的大小和位置。web頁面中元素的佈局是相對的,所以一個元素的佈局發生變化,會聯動地引起其餘元素的佈局發生變化。好比,``元素的寬度的變化會影響其子元素的寬度,其子元素寬度的變化也會繼續對其孫子元素產生影響。所以對於瀏覽器來講,佈局過程是常常發生的。
繪製。繪製,本質上就是填充像素的過程。包括繪製文字、顏色、圖像、邊框和陰影等,也就是一個DOM元素全部的可視效果。通常來講,這個繪製過程是在多個層上完成的。
渲染層合併。由上一步可知,對頁面中DOM元素的繪製是在多個層上進行的。在每一個層上完成繪製過程以後,瀏覽器會將全部層按照合理的順序合併成一個圖層,而後顯示在屏幕上。對於有位置重疊的元素的頁面,這個過程尤爲重要,由於一旦圖層的合併順序出錯,將會致使元素顯示異常。
若是你修改一個DOM元素的「paint only」屬性,好比背景圖片、文字顏色或陰影等,這些屬性不會影響頁面的佈局,所以瀏覽器會在完成樣式計算以後,跳過佈局過程,只作繪製和渲染層合併過程。
若是你修改一個非樣式且非繪製的CSS屬性,那麼瀏覽器會在完成樣式計算以後,跳過佈局和繪製的過程,直接作渲染層合併。這種方式在性能上是最理想的,對於動畫和滾動這種負荷很重的渲染,咱們要爭取使用這種渲染流程。
繪製,是填充像素的過程,這些像素將最終顯示在用戶的屏幕上。一般,這個過程是整個渲染流水線中耗時最長的一環,所以也是最須要避免發生的一環。
CSS屬性中,除了transform和opacity以外,修改任何屬性都會觸發繪製
若是佈局被觸發,那麼接下來繪製必定會被觸發。由於改變一個元素的幾何屬性就意味着該元素的全部像素都須要從新渲染!
若是改變元素的非幾何屬性,也可能觸發繪製,好比背景、文字顏色或者陰影效果,儘管這些屬性的改變不會觸發佈局。
繪製並不是老是在內存中的單層畫面裏完成的。實際上,瀏覽器在必要時將會把一幀畫面繪製成多層畫面,而後將這若干層畫面合併成一張圖片顯示到屏幕上。
這種繪製方式的好處是,使用tranforms來實現移動效果的元素將會被正常繪製,同時不會觸發對其餘元素的繪製。
在頁面中建立一個新的渲染層的最好方式就是使用CSS屬性will-change
,Chrome/Opera/Firefox都支持該屬性。同時再與transform
屬性一塊兒使用,就會建立一個新的組合層:
.moving-element { will-change: transform; }
對於那些目前還不支持will-change
屬性、但支持建立渲染層的瀏覽器,好比Safari和Mobile Safari,你可使用一個3D transform屬性來強制瀏覽器建立一個新的渲染層:
.moving-element { transform: translateZ(0); }
但須要注意的是:不要建立太多的渲染層。由於每建立一個新的渲染層,就意味着新的內存分配和更復雜的層的管理。
上面提到的,因爲框架的限制,頁面佈局相對需求來講複雜不少。開發中應該儘可能避免複雜的DOM結構,複雜的DOM結構更容易引發大面積的重繪。
渲染層的合併,就是把頁面中完成了繪製過程的部分合併成一層,而後顯示在屏幕上。
使用transform/opacity來實現動畫效果,目前只有transforms和opacity這兩個屬性不會觸發瀏覽器的佈局和繪製,對網頁元素這兩個屬性的修改會直接觸發渲染層合併。
對於動畫效果的實現,避免使用setTimeout或setInterval,請使用requestAnimationFrame。
把耗時長的JavaScript代碼放到Web Workers中去作。
這裏可使用Chrome DevTools的Timeline和JavaScript Profiler來分析JavaScript的性能。
性能優化是一門作減法的藝術。咱們首要要盡力簡化頁面渲染過程,而後要使渲染過程的每一步都儘可能高效。