首先簡單瞭解一下瀏覽器請求、加載、渲染一個頁面的大體過程: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(重繪):改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性時,屏幕的一部分要重畫,可是元素的幾何尺寸沒有變。
注意:
再參考一張圖理解一下:
細緻分離兩個環節,其餘環節參考上述概念註解:
JavaScript
:JavaScript實現動畫效果,DOM元素操做等。Composite(渲染層合併)
:對頁面中 DOM 元素的繪製是在多個層上進行的。在每一個層上完成繪製過程以後,瀏覽器會將全部層按照合理的順序合併成一個圖層,而後顯示在屏幕上。對於有位置重疊的元素的頁面,這個過程尤爲重要,由於一旦圖層的合併順序出錯,將會致使元素顯示異常。
在實際場景下,大體會出現三種常見的渲染流程(Layout和Paint步驟是可避免的,可參考上一張圖的注意部分理解):
注意:首先說明,這裏討論的是 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 中其實有幾種不一樣的層類型:
在瀏覽器渲染流程中提到了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提高爲了合成層就會有本身的繪圖上下文,而且會開啓硬件加速,有利於性能提高,裏面列舉了一些特色
注意:
總結合成層的優點:通常一個元素開啓硬件加速後會變成合成層,能夠獨立於普通文檔流中,改動後能夠避免整個頁面重繪,提高性能。
性能優化點:
提高合成層的最好方式是使用 CSS 的 will-change屬性。從上一節合成層產生緣由中,能夠知道 will-change 設置爲opacity、transform、top、left、bottom、right 能夠將元素提高爲合成層。
而對於固定不變的區域,咱們指望其並不會被重繪,所以能夠經過以前的方法,將其提高爲獨立的合成層。減小繪製區域,須要仔細分析頁面,區分繪製區域,減小重繪區域甚至避免重繪。
使用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硬件加速也有坑