重繪,迴流和合成,了解基本瀏覽器繪製幫你優化頁面性能
1、repaint/reflow 重繪和迴流
我們看到的頁面是由dom樹加上樣式來確定一個個盒模型的。現代瀏覽器例如chrome,解析html文檔後會先查看頁面內的link外聯css,等到css載入解析完後渲染dom樹。這就是為什麼許多頁面性能指南有一條是css寫在頂部,因為這個是早載入完了早渲染,要不頁面就白屏的狀態。dom樹載入渲染好後,我們的工作總是要來破壞這一片和諧的景象,這個元素漸漸放大,頂部飛下來個廣告等等需求。
在CSS3不那麼普及的IE8時代中,我們做頁面動畫基本都是利用js定時器改變元素位置來做類似平移,幻燈等效果。用改變屬性值的辦法我們難免會觸發代價高昂的repaint/reflow。
repaint,就是瀏覽器得知元素產生了不影響排版的情況下後對這個元素進行重新繪製的過程。例如我們改變了元素的顏色,加個下劃線等。
reflow, 瀏覽器得知元素產生了對文檔樹排版有影響的樣式變化,對所有受影響的dom節點進行重新排版工作
reflow的開銷大於repaint,所以用marginLeft, width ,height等屬性改變dom時我們要注意減少影響的範圍。基本原則就是,把動畫元素用position:absolute踢出文檔流,這樣R&R就限制在了absolute元素的子節點。告訴瀏覽器,我這塊結構跟其他的單獨渲染,不要攪和全頁面了。
具體更多的關於repaint/reflow介紹,有一篇百度的博文寫的很好,感興趣的同學可以移步觀摩:如何減少瀏覽器repaint和reflow(上)
2、compositor layer 合成渲染層
合成渲染,聽著可能有些陌生,但是你肯定用過。對於transform/opacity 這兩種變換,瀏覽器不會用repaint/reflow處理,而是在已經渲染好的元素基礎上進行附加工作。例如一個黑底色的div,往右飛100px, 傳統JS過程是對每次修改left值後重新畫一個div。而如果我們用transform:translate(0,100px) ,transition:2s 瀏覽器則是把這個繪製好的div單獨放在一個畫面層再平移這個層過去,div的幾何形狀,顏色不會再重複計算,而是保留在這個圖層中。Google開發者的一篇文章介紹了合成渲染的好處,其中有圖描述了理想動畫效果的流程
js改變樣式,樣式只觸發合成屬性,不觸發 repaint/reflow.附原文鏈接https://developers.google.com/web/fundamentals/performance/rendering/stick-to-compositor-only-properties-and-manage-layer-count
如果你想進一步集中用戶的顯卡資源來渲染你的動畫,給動畫元素使用translate3d屬性或者簡單加上translateZ屬性,讓瀏覽器以為這是要3d變換的元素,會讓瀏覽器單獨開合成層渲染你的動畫效果。集中資源的好處是動畫過程會更加平滑。
在實際應用中,我總結的經驗就是PC可以稍微放縱性能,大塊的dom改變也不會有感知。但是對於移動端,除非特別特殊的需求,比如一個元素斜拋拋物線軌跡運行,用js相對方便,其他都要用合成渲染。
類似半個手機屏幕大的元素平移,repaint在IPhone也吃不消。
3、監控你的合成層
前面介紹了H5動畫的性能技巧,但是在目前h5效果的主戰場,各大手機Webview中,也不是合成層就可以隨便用。
例如這個外賣h5版的右側菜單欄,開始版本是一個iscroll把店鋪內所有菜品都放在一起滾動。快餐商家只有20-30個菜品的時候表現良好,但是後來有小賣鋪接入數據,一個菜單欄動輒100條數據的時候,在華為P9手機的瀏覽器里就發現一滑動菜單就開始閃爍+抖動。排查了repaint/reflow,觸摸事件響應等bug後,試著刪除菜品dom到50個以內後,藥到病除~。為什麼單獨渲染層還會抖動呢?這是因為合成耗費的資源和變化元素的dom也是正相關。如何量化合成層的耗費?打開要檢測的動畫,用chrome開發者模式查看。
動畫開始前,切換到timeline標籤。選中2,記錄繪製信息。點擊1,開始錄製。然後做動畫操作。完成一個動畫動作後再按1,停止錄製。這時候能看到渲染的時間軸。我們節選一段時間的信息後,點擊3部分可以選中某一時間幀。在底部就可以看到tab簽多了個layers選項,就可以查看當前幀渲染的層數以及每個層對應的內存耗費。看到哪些層可以優化的,就果斷減dom,減層數,減變換,達到解決卡頓的目的。最後附上文章開頭處的相冊源碼,GitHub - sexdevil/photoViewer 作者我本來是懶,只是縮放圖片用transform scale,平移滑動等都是 marginLeft ,left 來回修改,結果跑起來以後被手機卡的教做人了,不得不把每個變換都匯總成transfrom一起渲染。源碼為開發環境,實際生產環境在webpack打包後只有20k,走gzip後只有7k.
推薦閱讀:
※性能優化之組件懶載入: Vue Lazy Component 介紹
※該把JS文件放在HTML文檔的那個位置
※解密Angular WebWorker Renderer (一)
※2D圓形隨機分布
※興趣部落的前端性能優化實踐概覽