瀏覽器渲染流程&Composite(渲染層合併)簡單總結

梳理瀏覽器渲染流程

首先簡單瞭解一下瀏覽器請求、加載、渲染一個頁面的大體過程:css

  • DNS 查詢
  • TCP 鏈接
  • HTTP 請求即響應
  • 服務器響應
  • 客戶端渲染

這裏主要將客戶端渲染展開梳理一下,從瀏覽器器內核拿到內容(渲染線程接收請求,加載網頁並渲染網頁),渲染大概能夠劃分紅如下幾個步驟:html

  • 解析html創建dom樹
  • 解析css構建render樹(將CSS代碼解析成樹形的數據結構,而後結合DOM合併成render樹)
  • 佈局render樹(Layout/reflow),負責各元素尺寸、位置的計算
  • 繪製render樹(paint),繪製頁面像素信息
  • 瀏覽器會將各層的信息發送給GPU(GPU進程:最多一個,用於3D繪製等),GPU會將各層合成(composite),顯示在屏幕上。

參考一張圖(webkit渲染主要流程):前端

圖片描述

這裏先解釋一下幾個概念,方便你們理解:html5

  DOM Tree:瀏覽器將HTML解析成樹形的數據結構。git

  CSS Rule Tree:瀏覽器將CSS解析成樹形的數據結構。github

  Render Tree: DOM和CSSOM合併後生成Render Tree。web

  layout: 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關係,從而去計算出每一個節點在屏幕中的位置。chrome

  painting: 按照算出來的規則,經過顯卡,把內容畫到屏幕上。canvas

  reflow(迴流):當瀏覽器發現某個部分發生了點變化影響了佈局,須要倒回去從新渲染,內行稱這個回退的過程叫 reflow。reflow 會從 <html> 這個 root frame 開始遞歸往下,依次計算全部的結點幾何尺寸和位置。reflow 幾乎是沒法避免的。如今界面上流行的一些效果,好比樹狀目錄的摺疊、展開(實質上是元素的顯 示與隱藏)等,都將引發瀏覽器的 reflow。鼠標滑過、點擊……只要這些行爲引發了頁面上某些元素的佔位面積、定位方式、邊距等屬性的變化,都會引發它內部、周圍甚至整個頁面的從新渲 染。一般咱們都沒法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響着。api

  repaint(重繪):改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性時,屏幕的一部分要重畫,可是元素的幾何尺寸沒有變。

注意:

  1. display:none 的節點不會被加入Render Tree,而visibility: hidden
    則會,因此,若是某個節點最開始是不顯示的,設爲display:none是更優的。
  2. display:none 會觸發 reflow,而 visibility:hidden 只會觸發 repaint,由於沒有發現位置變化。
  3. 有些狀況下,好比修改了元素的樣式,瀏覽器並不會馬上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 屬性
  • 使用加速視頻解碼的 <video> 元素 擁有 3D
  • (WebGL) 上下文或加速的 2D 上下文的 <canvas> 元素
  • 混合插件(如 Flash)
  • 對本身的 opacity 作 CSS動畫或使用一個動畫變換的元素
  • 擁有加速 CSS 過濾器的元素
  • 元素有一個包含複合層的後代節點(換句話說,就是一個元素擁有一個子元素,該子元素在本身的層裏)
  • 元素有一個z-index較低且包含一個複合層的兄弟元素(換句話說就是該元素在複合層上面渲染)

合成層的優勢

淘寶的栗子舉的很詳細,值得一看,裏面提到了一旦renderLayer提高爲了合成層就會有本身的繪圖上下文,而且會開啓硬件加速,有利於性能提高,裏面列舉了一些特色

  • 合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
  • 當須要 repaint 時,只須要 repaint 自己,不會影響到其餘的層
  • 對於 transform 和 opacity 效果,不會觸發 layout 和 paint

注意:

  1. 提高到合成層後合成層的位圖會交GPU處理,但請注意,僅僅只是合成的處理(把繪圖上下文的位圖輸出進行組合)須要用到GPU,生成合成層的位圖處理(繪圖上下文的工做)是須要CPU。
  2. 當須要repaint的時候能夠只repaint自己,不影響其餘層,可是paint以前還有style, layout,那就意味着即便合成層只是repaint了本身,但style和layout自己就很佔用時間。
  3. 僅僅是transform和opacity不會引起layout 和paint,那麼其餘的屬性不肯定。

總結合成層的優點:通常一個元素開啓硬件加速後會變成合成層,能夠獨立於普通文檔流中,改動後能夠避免整個頁面重繪,提高性能。

性能優化點:

  1. 提高動畫效果的元素 合成層的好處是不會影響到其餘元素的繪製,所以,爲了減小動畫元素對其餘元素的影響,從而減小paint,咱們須要把動畫效果中的元素提高爲合成層。 提高合成層的最好方式是使用 CSS 的 will-change屬性。從上一節合成層產生緣由中,能夠知道 will-change 設置爲opacity、transform、top、left、bottom、right 能夠將元素提高爲合成層。
  2. 使用 transform 或者 opacity 來實現動畫效果, 這樣只須要作合成層的合併就行了。
  3. 減小繪製區域 對於不須要從新繪製的區域應儘可能避免繪製,以減小繪製區域,好比一個 fix 在頁面頂部的固定不變的導航header,在頁面內容某個區域 repaint 時,整個屏幕包括 fix 的 header 也會被重繪。而對於固定不變的區域,咱們指望其並不會被重繪,所以能夠經過以前的方法,將其提高爲獨立的合成層。減小繪製區域,須要仔細分析頁面,區分繪製區域,減小重繪區域甚至避免重繪。

利用合成層可能踩到的坑

  1. 合成層佔用內存的問題
  2. 層爆炸,因爲某些緣由可能致使產生大量不在預期內的合成層,雖然有瀏覽器的層壓縮機制,可是也有不少沒法進行壓縮的狀況,這就可能出現層爆炸的現象(簡單理解就是,不少不須要提高爲合成層的元素由於某些不當操做成爲了合成層)。解決層爆炸的問題,最佳方案是打破 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硬件加速也有坑

相關文章
相關標籤/搜索