摘要:渲染層合并對頁面中元素的繪制是在多個層上進行的。擁有兩套不同的渲染路徑硬件加速路徑和舊軟件路徑中有不同類型的層負責子樹和負責的子樹,只有是作為紋理上傳給的。整個圖在中其實有幾種不同的層類型渲染層,這是負責對應子樹圖形層,這是負責對應子樹。
梳理瀏覽器渲染流程
首先簡單了解一下瀏覽器請求、加載、渲染一個頁面的大致過程:
DNS 查詢
TCP 連接
HTTP 請求即響應
服務器響應
客戶端渲染
這里主要將客戶端渲染展開梳理一下,從瀏覽器器內核拿到內容(渲染線程接收請求,加載網頁并渲染網頁),渲染大概可以劃分成以下幾個步驟:
解析html建立dom樹
解析css構建render樹(將CSS代碼解析成樹形的數據結構,然后結合DOM合并成render樹)
布局render樹(Layout/reflow),負責各元素尺寸、位置的計算
繪制render樹(paint),繪制頁面像素信息
瀏覽器會將各層的信息發送給GPU(GPU進程:最多一個,用于3D繪制等),GPU會將各層合成(composite),顯示在屏幕上。
參考一張圖(webkit渲染主要流程):
這里先解釋一下幾個概念,方便大家理解:
DOM Tree:瀏覽器將HTML解析成樹形的數據結構。
CSS Rule Tree:瀏覽器將CSS解析成樹形的數據結構。
Render Tree: DOM和CSSOM合并后生成Render Tree。
layout: 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關系,從而去計算出每個節點在屏幕中的位置。
painting: 按照算出來的規則,通過顯卡,把內容畫到屏幕上。
reflow(回流):當瀏覽器發現某個部分發生了點變化影響了布局,需要倒回去重新渲染,內行稱這個回退的過程叫 reflow。reflow 會從 這個 root frame 開始遞歸往下,依次計算所有的結點幾何尺寸和位置。reflow 幾乎是無法避免的。現在界面上流行的一些效果,比如樹狀目錄的折疊、展開(實質上是元素的顯 示與隱藏)等,都將引起瀏覽器的 reflow。鼠標滑過、點擊……只要這些行為引起了頁面上某些元素的占位面積、定位方式、邊距等屬性的變化,都會引起它內部、周圍甚至整個頁面的重新渲 染。通常我們都無法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響著。
repaint(重繪):改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部布局的屬性時,屏幕的一部分要重畫,但是元素的幾何尺寸沒有變。
注意:
display:none 的節點不會被加入Render Tree,而visibility: hidden
則會,所以,如果某個節點最開始是不顯示的,設為display:none是更優的。
display:none 會觸發 reflow,而 visibility:hidden 只會觸發 repaint,因為沒有發現位置變化。
有些情況下,比如修改了元素的樣式,瀏覽器并不會立刻reflow 或 repaint 一次,而是會把這樣的操作積攢一批,然后做一次reflow,這又叫異步 reflow 或增量異步 reflow。但是在有些情況下,比如resize窗口,改變了頁面默認的字體等。對于這些操作,瀏覽器會馬上進行 reflow。
再參考一張圖理解一下:
細致分離兩個環節,其他環節參考上述概念注解:
JavaScript:JavaScript實現動畫效果,DOM元素操作等。
Composite(渲染層合并):對頁面中 DOM 元素的繪制是在多個層上進行的。在每個層上完成繪制過程之后,瀏覽器會將所有層按照合理的順序合并成一個圖層,然后顯示在屏幕上。對于有位置重疊的元素的頁面,這個過程尤其重要,因為一旦圖層的合并順序出錯,將會導致元素顯示異常。
在實際場景下,大致會出現三種常見的渲染流程(Layout和Paint步驟是可避免的,可參考上一張圖的注意部分理解):
Composite 了解層注意:首先說明,這里討論的是 WebKit,描述的是 Chrome 的實現細節,而并非是 web 平臺的功能,因此這里介紹的內容不一定適用于其他瀏覽器。
Chrome 擁有兩套不同的渲染路徑(rendering path):硬件加速路徑和舊軟件路徑(older software path)
Chrome 中有不同類型的層: RenderLayer(負責 DOM 子樹)和GraphicsLayer(負責 RenderLayer的子樹),只有 GraphicsLayer 是作為紋理(texture)上傳給GPU的。
什么是紋理?可以把它想象成一個從主存儲器(例如 RAM)移動到圖像存儲器(例如 GPU 中的 VRAM)的位圖圖像(bitmapimage)
Chrome 使用紋理來從 GPU上獲得大塊的頁面內容。通過將紋理應用到一個非常簡單的矩形網格就能很容易匹配不同的位置(position)和變形(transformation)。這也就是3DCSS 的工作原理,它對于快速滾動也十分有效。
整個圖:
在 Chrome 中其實有幾種不同的層類型:
RenderLayers 渲染層,這是負責對應 DOM 子樹
GraphicsLayers 圖形層,這是負責對應 RenderLayers子樹。
在瀏覽器渲染流程中提到了composite概念,在 DOM 樹中每個節點都會對應一個 LayoutObject,當他們的 LayoutObject 處于相同的坐標空間時,就會形成一個 RenderLayers ,也就是渲染層。RenderLayers 來保證頁面元素以正確的順序合成,這時候就會出現層合成(composite),從而正確處理透明元素和重疊元素的顯示。
某些特殊的渲染層會被認為是合成層(Compositing Layers),合成層擁有多帶帶的 GraphicsLayer,而其他不是合成層的渲染層,則和其第一個擁有 GraphicsLayer 父層公用一個。
而每個GraphicsLayer(合成層多帶帶擁有的圖層) 都有一個 GraphicsContext,GraphicsContext 會負責輸出該層的位圖,位圖是存儲在共享內存中,作為紋理上傳到 GPU 中,最后由 GPU 將多個位圖進行合成,然后顯示到屏幕上。
如何變成合成層合成層創建標準合成層的優點什么情況下能使元素獲得自己的層?雖然 Chrome的啟發式方法(heuristic)隨著時間在不斷發展進步,但是從目前來說,滿足以下任意情況便會創建層:
3D 或透視變換(perspective transform) CSS 屬性
使用加速視頻解碼的
(WebGL) 上下文或加速的 2D 上下文的
混合插件(如 Flash)
對自己的 opacity 做 CSS動畫或使用一個動畫變換的元素
擁有加速 CSS 過濾器的元素
元素有一個包含復合層的后代節點(換句話說,就是一個元素擁有一個子元素,該子元素在自己的層里)
元素有一個z-index較低且包含一個復合層的兄弟元素(換句話說就是該元素在復合層上面渲染)
淘寶的栗子舉的很詳細,值得一看,里面提到了一旦renderLayer提升為了合成層就會有自己的繪圖上下文,并且會開啟硬件加速,有利于性能提升,里面列舉了一些特點
合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
對于 transform 和 opacity 效果,不會觸發 layout 和 paint
注意:
提升到合成層后合成層的位圖會交GPU處理,但請注意,僅僅只是合成的處理(把繪圖上下文的位圖輸出進行組合)需要用到GPU,生成合成層的位圖處理(繪圖上下文的工作)是需要CPU。
當需要repaint的時候可以只repaint本身,不影響其他層,但是paint之前還有style, layout,那就意味著即使合成層只是repaint了自己,但style和layout本身就很占用時間。
僅僅是transform和opacity不會引發layout 和paint,那么其他的屬性不確定。
總結合成層的優勢:一般一個元素開啟硬件加速后會變成合成層,可以獨立于普通文檔流中,改動后可以避免整個頁面重繪,提升性能。
性能優化點:
提升動畫效果的元素 合成層的好處是不會影響到其他元素的繪制,因此,為了減少動畫元素對其他元素的影響,從而減少paint,我們需要把動畫效果中的元素提升為合成層。 提升合成層的最好方式是使用 CSS 的 will-change屬性。從上一節合成層產生原因中,可以知道 will-change 設置為opacity、transform、top、left、bottom、right 可以將元素提升為合成層。
使用 transform 或者 opacity 來實現動畫效果, 這樣只需要做合成層的合并就好了。
減少繪制區域 對于不需要重新繪制的區域應盡量避免繪制,以減少繪制區域,比如一個 fix 在頁面頂部的固定不變的導航header,在頁面內容某個區域 repaint 時,整個屏幕包括 fix 的 header 也會被重繪。而對于固定不變的區域,我們期望其并不會被重繪,因此可以通過之前的方法,將其提升為獨立的合成層。減少繪制區域,需要仔細分析頁面,區分繪制區域,減少重繪區域甚至避免重繪。
利用合成層可能踩到的坑合成層占用內存的問題
層爆炸,由于某些原因可能導致產生大量不在預期內的合成層,雖然有瀏覽器的層壓縮機制,但是也有很多無法進行壓縮的情況,這就可能出現層爆炸的現象(簡單理解就是,很多不需要提升為合成層的元素因為某些不當操作成為了合成層)。解決層爆炸的問題,最佳方案是打破 overlap 的條件,也就是說讓其他元素不要和合成層元素重疊。簡單直接的方式:使用3D硬件加速提升動畫性能時,最好給元素增加一個z-index屬性,人為干擾合成的排序,可以有效減少chrome創建不必要的合成層,提升渲染性能,移動端優化效果尤為明顯。 在這篇文章中的demo可以看出其中厲害。
用chremo打開demo頁面后,開啟瀏覽器的開發者模式,再按照如圖操作打開查看工具:
開啟 Rendering 的Layer borders后 觀察點擊為動畫元素設置z-index復選框的頁面提示變化:
上圖中可以明顯看出:頁面中設置了一個h1標題,應用了translate3d動畫,使得它被放到composited layer中渲染,然后在這個元素后面創建了2000個list。在不為h1元素設置z-index的情況下,使得本不需要提升到合成層的ul元素下的每個li元素都提升為一個多帶帶合成層(每個li元素的黃色提示邊框),最終會導致GPU資源過度消耗頁面滑動時很卡,尤其在移動端(安卓)上更加明顯。
如上圖操作選中為動畫元素設置z-index,可以看出ul下的每個li都回歸到普通渲染層,不再是合成層也就不會消耗GPU資源去渲染,從而達到了優化頁面性能優化的目的。
大家可以用支持『硬件加速』的『安卓』手機瀏覽器測試上述頁面,給動畫元素加z-index前后的性能差距非常明顯。
最后在實際的前端開發中尤其是移動端開發,很多小伙伴都很喜歡使用類似 translateZ(0)等屬性來進行所謂的硬件加速,以提升性能,達到優化頁面動態效果的目的,但還是要注意凡事過猶不及,應用硬件加速的同時也要注意到千萬別踩坑。
關于合成層的更細致具體的講解,可以仔細學習下下面的參考文章(尤其是前三篇哦)。
最后祝愿熱愛技術的你我始終堅持在探索技術的路上奮力前行!
參考文章:
無線性能優化:Composite
DOM to Screen
CSS GPU Animation: Doing It Right
web優化之composite
詳談層合成(composite)
CSS3硬件加速也有坑
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94471.html
摘要:渲染層合并對頁面中元素的繪制是在多個層上進行的。擁有兩套不同的渲染路徑硬件加速路徑和舊軟件路徑中有不同類型的層負責子樹和負責的子樹,只有是作為紋理上傳給的。整個圖在中其實有幾種不同的層類型渲染層,這是負責對應子樹圖形層,這是負責對應子樹。 梳理瀏覽器渲染流程 首先簡單了解一下瀏覽器請求、加載、渲染一個頁面的大致過程: DNS 查詢 TCP 連接 HTTP 請求即響應 服務器響應 客戶...
摘要:渲染層合并對頁面中元素的繪制是在多個層上進行的。擁有兩套不同的渲染路徑硬件加速路徑和舊軟件路徑中有不同類型的層負責子樹和負責的子樹,只有是作為紋理上傳給的。整個圖在中其實有幾種不同的層類型渲染層,這是負責對應子樹圖形層,這是負責對應子樹。 梳理瀏覽器渲染流程 首先簡單了解一下瀏覽器請求、加載、渲染一個頁面的大致過程: DNS 查詢 TCP 連接 HTTP 請求即響應 服務器響應 客戶...
摘要:但是還是會阻塞事件,所以會可能在觸發前或后執行,但是一定會在事件前觸發。當監聽到該圖片元素進入可視窗口時,即將自定義屬性中的地址存儲到屬性中,達到懶加載的效果。當代碼執行,線程被凍結。所以的性能讓變慢。 概括 涉及到的分類 網絡層面 構建層面 瀏覽器渲染層面 服務端層面 涉及到的功能點 資源的合并與壓縮 圖片編解碼原理和類型選擇 瀏覽器渲染機制 懶加載預加載 瀏覽器存儲 緩存機制...
閱讀 1983·2021-11-24 09:39
閱讀 978·2021-11-11 16:55
閱讀 1425·2021-10-09 09:43
閱讀 1415·2021-10-08 10:17
閱讀 1648·2021-08-25 09:41
閱讀 420·2019-08-30 13:02
閱讀 627·2019-08-29 15:14
閱讀 1001·2019-08-29 13:53