深入理解瀏覽器工作原理

-童夢發表於2020-10-27

前面的話

  瀏覽器(browser application)是專門用來訪問和瀏覽全球資訊網頁面的客戶端軟體,也是現代計算機系統中應用最為廣泛的軟體之一,其重要性不言而喻。前端工程師作為負責程式頁面顯示的工程師,需要直接與瀏覽器打交道。本文將詳細介紹瀏覽器的工作原理

 

組成

  瀏覽器的組成如下圖所示

browser

  主要元件包括:

  1. 使用者介面 - 包括位址列、後退/前進按鈕、書籤目錄等,也就是所看到的除了用來顯示所請求頁面的主視窗之外的其他部分

  2. 瀏覽器引擎 - 用來查詢及操作渲染引擎的介面

  3. 渲染引擎 - 用來顯示請求的內容,例如,如果請求內容為html,它負責解析html及css,並將解析後的結果顯示出來。

  4. 網路 - 用來完成網路呼叫,例如http請求,它具有平臺無關的介面,可以在不同平臺上工作。

  5. UI後端 - 用來繪製類似組合選擇框及對話方塊等基本元件,具有不特定於某個平臺的通用介面,底層使用作業系統的使用者介面。

  6. JS直譯器 - 用來解釋執行JS程式碼。

  7. 資料儲存 - 屬於持久層,瀏覽器需要在硬碟中儲存類似cookie的各種資料,HTML5定義了web database技術,這是一種輕量級完整的客戶端儲存技術

 

核心

  瀏覽器核心分成兩部分:渲染引擎和js引擎,由於js引擎越來越獨立,核心就傾向於只指渲染引擎,負責請求網路頁面資源加以解析排版並呈現給使用者

  預設情況下,渲染引擎可以顯示html、xml文件及圖片,它也可以藉助外掛顯示其他型別資料,例如使用PDF閱讀器外掛,可以顯示PDF格式

【渲染引擎】

  firefox使用gecko引擎

  IE使用Trident引擎,2015年微軟推出自己新的瀏覽器,原名叫斯巴達,後改名edge,使用edge引擎

  opera最早使用Presto引擎,後來棄用

  chrome\safari\opera使用webkit引擎,13年chrome和opera開始使用Blink引擎

  UC使用U3引擎

  QQ瀏覽器和微信核心使用X5引擎,16年開始使用Blink引擎

【js引擎】

  老版本IE使用Jscript引擎,IE9之後使用Chakra引擎,edge瀏覽器仍然使用Chakra引擎

  firefox使用monkey系列引擎

  safari使用的SquirrelFish系列引擎

  Opera使用Carakan引擎

  chrome使用V8引擎。nodeJs其實就是封裝了V8引擎

 

渲染流程

  從資源的下載到最終的頁面展現,渲染流程可簡單地理解成一個線性串聯的變換過程的組合,原始輸入為URL地址,最終輸出為頁面Bitmap,中間依次經過了Loader、Parser、Layout和Paint模組

chrome1

  渲染引擎的核心流程如下所示

chrome

【Loader】

  Loader模組負責處理所有的HTTP請求以及網路資源的快取,相當於是從URL輸入到Page Resource輸出的變換過程。HTML頁面中通常有外鏈的JS/CSS/Image資源,為了不阻塞後續解析過程,一般會有兩個IO管道同時存在,一個負責主頁面下載,一個負責各種外鏈資源的下載

chrome

  注意:雖然大部分情況下不同資源可以併發下載非同步解析(如圖片資源可以在主頁面解析顯示完成後再被顯示),但JS指令碼可能會要求改變頁面,因此有時保持執行順序和下載管道後續處理的阻塞是不可避免的

【Parser】

  1、解析HTML

  Parser模組主要負責解析HTML頁面,完成從HTML文字到HTML語法樹再到文件物件樹(Document Object Model Tree,DOM Tree)的對映過程

  HTML語法樹生成是一個典型的語法解析過程,可以分成兩個子過程:詞法解析和語法解析

  詞法解析按照詞法規則(如正規表示式)將HTML文字分割成大量的標記(token),並去除其中無關的字元如空格。語法解析按照語法規則(如上下文無關文法)匹配Token序列生成語法樹,通常有自上而下和自下而上兩種匹配方式

  瀏覽器核心中對HTML頁面真正的內部表示並不是語法樹,而是W3C組織規範的文件物件模型(Document Object Model,DOM)。DOM也是樹形結構,以Document物件為根。DOM節點基本和HTML語法樹節點一一對應,因此在語法解析過程中,通常直接生成最終的DOM樹

  2、解析CSS

  頁面中所有的CSS由樣式表CSSStyleSheet集合構成,而CSSStyleSheet是一系列CSSRule的集合,每一條CSSRule則由選擇器CSSStyleSelector部分和宣告CSSStyleDeclaration部分構成,而CSSStyleDeclaration是CSS屬性和值的Key-Value集合

  CSS解析完畢後會進行CSSRule的匹配過程,即尋找滿足每條CSS規則Selector部分的HTML元素,然後將其Declaration部分應用於該元素。實際的規則匹配過程會考慮到預設和繼承的CSS屬性、匹配的效率及規則的優先順序等因素

  3、解析Javascript

  JavaScript一般由單獨的指令碼引擎解析執行,它的作用通常是動態地改變DOM樹(比如為DOM節點新增事件響應處理函式),即根據時間(timer)或事件(event)對映一棵DOM樹到另一棵DOM樹。

  簡單來說,經過了Parser模組的處理,核心把頁面文字轉換成了一棵節點帶CSS Style、會響應自定義事件的Styled DOM樹

【layout】

  Layout過程就是排版,它包含兩大過程

  1、建立渲染樹

  佈局樹(或者叫做渲染樹、Render Tree)和DOM樹大體能一一對應,兩者在核心中同時存在但作用不同。DOM樹是HTML文件的物件表示,同時也作為JavaScript操縱HTML的物件介面。Render樹是DOM樹的排版表示,用以計算可視DOM節點的佈局資訊(如寬、高、座標)和後續階段的繪製顯示

  注意:並非所有DOM節點都可視,也就是並非所有DOM樹節點都會對應生成一個Render樹節點。例如head標籤(HTMLHeadElement節點)不表示任何排版區域,因而沒有對應的Render節點。同時,DOM樹可視節點的CSS Style就是其對應Render樹節點的Style

chrome

  2、計算佈局

  佈局就是安排和計算頁面中每個元素大小位置等幾何資訊的過程。HTML採用流式佈局模型,基本的原則是頁面元素在順序遍歷過程中依次按從左至右、從上至下的排列方式確定各自的位置區域

  一個HTML元素對應一個以CSS盒子模型描述的方塊區域,HTML元素分成兩個基本型別,Inline和Block。Inline元素不會換行,按從左到右來佈局。Block元素的出現意味著需要從上至下換到下一行來佈局。除了這種基本的順序按照元素的Inline和Block來進行流式佈局之外,還有特殊指定的一些佈局方式,如Absolute/Fixed/Relative三種定位佈局以及Float浮動佈局

  簡單情況下,佈局可以順序遍歷一次Render樹完成,但也有需要迭代的情況。當祖先元素的大小位置依賴於後代元素或者互相依賴時,一次遍歷就無法完成佈局,如Table元素的寬高未明確指定而其下某一子元素Tr指定其高度為父Table高度的30%的情況

  經過了Layout階段的處理,把帶Style的DOM樹變換成包含佈局資訊和繪製資訊的Render樹,接下來的顯示工作就交由Paint模組進行操作了

【Paint】

  Paint模組負責將Render樹對映成可視的圖形,它會遍歷Render樹呼叫每個Render節點的繪製方法將其內容顯示在一塊畫布或者點陣圖上,並最終呈現在瀏覽器應用視窗中成為使用者看到的實際頁面。每個節點對應的大小位置等資訊都已經由Layout階段計算好了,節點的內容取決於對應的HTML元素,或是文字,或是圖片,或是UI控制元件

  通常情況下,佈局和繪製是相當耗時的操作。如果DOM樹每次略有改動都要重新佈局和繪製一次,效率會相當低下。因此,一般瀏覽核心都會實現一種增量佈局和增量繪製的方式。當一個DOM樹節點(或者它的子節點)內容或者樣式發生變化時,核心會確定其影響範圍,在佈局階段會標記出受該節點佈局影響的其他節點(比如可能是子節點),在繪製階段則會標記出一個Dirty區域並通知系統重繪

  按照HTML相關規範,頁面元素的CSS屬性也規定了其繪製順序,如根據不同Layer必須按順序繪製,否則覆蓋疊加效果會出現錯誤,如元素的邊框輪廓和內容背景的繪製次序也有規定

 

資源載入

  使用瀏覽器上網時,首先會在位址列輸入一個網址,瀏覽器會依據網址向伺服器傳送資源請求,伺服器解析請求,並將相關資料資源傳送回給瀏覽器,這些資料資源包括Page的描述文件、圖片、JavaScript指令碼、CSS等。此後,瀏覽器引擎會對資料進行解碼、解析、排版、繪製等操作,最終呈現出完整的頁面。Loader是瀏覽器的排頭兵,負責資源載入的工作

  Loader在瀏覽器中承上啟下,一方面它作為網路模組的客戶,通過網路模組來載入資源;另一方面它為Parser模組載入頁面的內容,控制著瀏覽器後續的解析以及繪製過程

chrome

  Loader有兩條資源載入路徑:主資源載入路徑和派生資源載入路徑。這兩類資源的載入過程頗有不同,比如對資源載入失敗的處理,主資源下載失敗會有報錯提示,而派生資源如圖片下載失敗,往往只顯示一個佔位

  在位址列輸入新地址或者在已經開啟的頁面中點選連結,都會觸發主資源的載入流程,隨著主資源在HTTP協議的傳輸下分段到達,瀏覽器的Parser模組解析主資源的內容,生成派生資源對應的DOM結構,然後根據需求觸發派生資源的載入流程。主資源的載入是立刻發起的,而派生資源則可能會為了優化網路,在佇列中等待

  主資源和派生資源的載入還有一個區別,在Android 4.2版本中主資源是沒有快取的,而派生資源是有快取機制的。這裡的快取指的是Memory Cache,用於儲存原始資料(比如CSS、JS等),以及解碼過的資料,通過Memory Cache可以節省網路請求和圖片解碼的時間

  瀏覽器在載入主資源後,主資源會被解碼,然後進行解析,生成DOM(文件物件模型)樹。在解析過程中,如果遇到<img的起始標籤,會建立相應的image元素HTMLImageElement,接著依據img標籤的內容設定HTMLImageElement的屬性。在設定src屬性時,會觸發圖片資源載入,發起載入資源請求

 

 

快取

  快取在瀏覽器中也得到了廣泛的應用,對提高使用者體驗起到了重要作用。在瀏覽器中,主要存在三種型別的快取:Page Cache、Memory Cache、Disk Cache。這三類Cache的容量都是可以配置的,比如限制Memory Cache最大不超過30MB,Page Cache快取的頁面數量不超過5個等

Page Cache:是將瀏覽的頁面狀態臨時儲存在快取中,以加速頁面返回等操作
Memory Cache:瀏覽器內部的快取機制,對於相同url的資源直接從快取中獲取,不需重新下載
Disk Cache:資源載入快取和伺服器進行互動,伺服器端可以通過HTTP頭資訊設定網頁要不要快取。

【記憶體快取】

  Memory Cache,顧名思義記憶體快取,其主要作用為快取頁面使用各種派生資源。在使用瀏覽器瀏覽網頁時,尤其是瀏覽一個大型網站的不同頁面時,經常會遇到網頁中包含相同資源的情況,應用Memory Cache可以顯著提高瀏覽器的使用者體驗,減少無謂的記憶體、時間以及網路頻寬開銷

【頁面快取】

  Page Cache,即頁面快取。用來快取使用者訪問過的網頁DOM樹、Render樹等資料。設計頁面快取的意圖在於提供流暢的頁面前進、後退瀏覽體驗。幾乎所有的現代瀏覽器都支援頁面快取功能

  如果瀏覽器沒有頁面快取,使用者點選連結訪問新頁面時,原頁面的各種派生資源、JavaScript物件、DOM樹節點等佔據的記憶體統統被回收,此後當使用者點選後退按鈕以瀏覽原頁面時,瀏覽器必須先要重新從網路下載相關資源,然後進行解碼、解析、佈局、渲染一系列操作,最後才能為使用者呈現出頁面,這無疑增加了使用者的等待時間,影響了使用者的使用體驗

  所有的派生資源載入時都會與Memory Cache關聯,如果Memory Cache中有資源的備份且條件合適,則可以直接從Memory Cache中載入。而Page Cache只會在使用者點選前進或後退按鈕時才會被查詢,如果頁面符合快取條件並被快取了,則直接從Page Cache中載入。即使某個需要被載入的頁面在Page Cache中有備份,但若觸發載入的原因是使用者在位址列輸入url或點選連結,則頁面仍然是通過網路載入。也就是說Page Cache並不是主資源的通用快取

【磁碟快取】

  Disk Cache,即磁碟快取。現代的瀏覽器基本都有磁碟快取機制,為了提升使用者的使用體驗,瀏覽器將下載的資源儲存到本地磁碟,當瀏覽器下次請求相同的資源時,可以省去網路下載資源的時間,直接從本地磁碟中取出資源即可

  磁碟快取即我們常說的Web快取,分為強快取和協商快取,它們的區別在於強快取不發請求到伺服器,協商快取會發請求到伺服器

 

網頁解析

  可以將瀏覽器整體看作一個網頁處理模組,這個模組的輸入是網路上接收到的位元組流形式的網頁內容。輸出是三棵樹型邏輯結構:DOM樹、Render樹及RenderLayer樹

  瀏覽器的解析過程就是將位元組流形式的網頁內容構建成DOM樹、Render樹及RenderLayer樹的過程

  瀏覽器的解析物件是網頁內容,網頁內容包括以下三個部分:

  1、HTML文件:超文字標記語言,製作Web頁面的標準語言

  2、CSS樣式表(Cascading Style Sheet):級聯樣式表,用來控制網頁樣式,並允許樣式資訊與網頁內容相分離的一種標記性語言

  3、JavaScript指令碼:JavaScript是一種無型別的解釋型指令碼語言。常用於為網頁新增動態功能

  HTML文件決定了DOM樹及Render樹的結構。CSS樣式表決定了Render樹上節點的排版佈局方式。JavaScript程式碼可以操作DOM樹,改變DOM樹的結構,也可以用來給頁面新增更豐富的動態功能

  HTML文件被解析生成DOM樹,由DOM節點建立Render樹節點時,會觸發CSS匹配過程,CSS匹配的結果是RenderStyle例項,這個例項由Render節點持有,儲存了Render節點的排版佈局資訊。CSS的解析過程即是CSS語法在瀏覽器的內部表示過程,解析的結果是得到一系列的CSS規則。CSS的匹配過程主要依據CSS選擇器的不同優先順序進行,高優先順序選擇器優先適用。根據網頁上定義的JavaScript指令碼的不同屬性,JavaScript指令碼的下載和執行時機會有所不同。JavaScript指令碼的執行是由渲染引擎轉交給JS引擎執行的。下面分別看一下HTML、CSS、JavaScript的具體解析和執行

【DOM樹構建】

  DOM(Document Object Model,文件物件模型),是中立於平臺和語言的介面。它允許程式和指令碼動態地訪問和更新文件的內容結構和樣式。DOM是頁面上資料和結構的一個樹形表示,使用DOM介面可以對DOM樹結構進行操作。DOM規範只是定義了程式設計介面,沒有對文件的表示方式做任何限制。以樹狀結構表示DOM文件是比較普遍的實現方式。這個樹狀結構就稱為DOM樹。DOM樹是DOM文件中的節點按照層次組織構成的。以HTML文件為例,每一個標籤都對應著DOM樹上的一個節點。由於是樹形結構表示,這些節點之間的關係也是通過父子或兄弟維繫的

  渲染引擎解析HTML文件的過程就是將位元組流形式的網頁內容解析成DOM Tree、Render Tree、Render Layer Tree三棵樹的過程。這個過程可以分為解碼、分詞、解析、建樹四個步驟

  1、解碼:將網路上接收到的經過編碼的位元組流,解碼成Unicode字元

  2、分詞:按照一定的切詞規則,將Unicode字元流切成一個個的詞語(Tokens)

  3、解析:根據詞語的語義,建立相應的節點(Node)

  4、建樹:將節點關聯到一起,建立DOM樹、Render樹和RenderLayer樹

【Render樹構建】

  Render樹用於表示文件的可視資訊,記錄了文件中每個可視元素的佈局及渲染方式。Render樹與DOM樹是同時建立的

  HTML頁面通過CSS控制頁面佈局,所以RenderObject需要知道自身的CSS屬性,CSSStyleSelector負責為元素提供RenderStyle。RenderObject包含自身的RenderStyle的引用。CSSStyleSelector是在CSS解析過程中生成的。Render節點建立後,就會被attach到Render樹上

  當前Render節點的父節點負責將當前Render節點插入到合適的位置,當父Render節點設定好當前Redner節點的前後兄弟節點後,當前Render節點就attach到了Render樹上

  RenderObject是Render樹所有節點的基類,作用類似於DOM樹的Node類。這個類儲存了繪製頁面可視元素所需要的樣式及佈局資訊,RenderObject物件及其子類都知道如何繪製自己。事實上繪製Render樹的過程就是RenderObject按照一定順序繪製自身的過程。DOM樹上的節點與Render樹上的節點並不是一一對應的。只有DOM樹的根節點及可視節點才會建立對應的RenderObject節點

【Render Layer樹構建】

  RenderLayer樹以層為節點組織文件的可視資訊,網頁上的每一層對應一個RenderLayer物件。RenderLayer樹可以看作Render樹的稀疏表示,每個RenderLayer樹的節點都對應著一棵Render樹的子樹,這棵子樹上所有Render節點都在網頁的同一層顯示

  RenderLayer樹是基於RenderObject樹構建的,滿足一定條件的RenderObject才會建立對應的RenderLayer節點。下面是RenderLayer節點的建立條件:

  1、網頁的root節點

  2、有顯式的CSS position屬性(relative,absolute,fixed)

  3、元素設定了transform

  4、元素是透明的,即opacity不等於1

  5、節點有溢位(overflow)、alpha mask或者反射(reflection)效果。

  6、元素有CSS filter(濾鏡)屬性

  7、2D Canvas或者WebGL

  8、Video元素

  當滿足這些條件之一時,RenderLayer例項被建立。RenderObject節點與RenderLayer節點是多對一的關係,即一個或多個RenderObject節點對應一個RenderLayer節點。這一點可以理解為網頁的一層中可包含一個或多個可視節點。RenderLayer樹的根節點是RenderView例項

  RenderLayer的一個重要用途是可以在繪製時實現合成加速,即每一個RenderLayer對應系統的一塊後端儲存,這樣在網頁內容發生更新時,可以只更新有變化的RenderLayer,從而提高渲染效率

【CSS解析】

  CSS解析過程即是將原始的CSS檔案中包含的一系列CSS規則表示成渲染引擎中相應規則類的例項的過程

chrome

  解析選擇器和解析屬性值的過程都可能執行多次。渲染引擎為解析出來的選擇器建立一個CSSSelector例項,由於可能存在多個選擇器,渲染引擎使用CSSSelectorList類儲存所有的選擇器,併為解析出來的每個屬性值對建立CSSProperty例項

  CSS檔案解析完成後,CSS規則都儲存在了CSSRuleList例項中,這些規則會在建立Render節點的過程中使用到。Node節點通過呼叫CSSStyleSelector例項的StyleForElement()函式為Render節點建立RenderStyle例項。有了RenderStyle例項才可以建立RenderObject例項。RenderStyle描述了RenderObject的排版佈局資訊,也就是匹配後的樣式資訊

chrome

  CSS規則匹配過程就發生在CSSStyleSelector建立RenderStyle例項的過程中。CSSStyleSelector負責從CSSRuleList中找出所有匹配相應元素的樣式屬性的Property-Value對

  CSS規則匹配是按照選擇器型別的優先順序進行的,不同型別的選擇器具有不同的優先順序。常用選擇器型別的優先順序如下:

ID選擇器 > 型別選擇器 > 標籤選擇器 > 相鄰選擇器 > 子選擇器 > 後代選擇器

  所有匹配上元素的CSSStyleRule都會放入一個結果陣列中。渲染引擎會對所有存入結果陣列中的規則按照選擇器的優先順序進行排序,高優先順序規則優先使用,最終使用的規則會用來建立RenderStyle例項。RenderStyle例項由RenderObject物件持有,RenderObject就是根據RenderStyle中包含的資訊,進行自身排版繪製

【JS執行】

  JavaScript是一種解釋型的動態指令碼語言,需要由專門的JavaScript引擎執行。Android 4.2版本的WebKit採用的JavaScript執行引擎為V8,V8是由Google支援的開源專案。它的設計目的就是追求更高的效能,最大限度地提高JavaScript的執行效率。與JavaScriptCore等傳統引擎不同,V8把JavaScript程式碼直接編譯成機器碼執行,比起傳統“中間程式碼+直譯器”的引擎,效能優勢非常明顯。JS程式碼通常儲存在獨立的JS檔案中,通過script標籤引用到HTML文件中

  DOM樹建立過程中遇到script標籤時會建立HTMLScriptElement例項。HTMLScript-Element的父類ScriptElement中包含了對JS指令碼的所有處理,包括下載、快取、執行等。根據script標籤的不同屬性,JS指令碼載入後的執行時機會有所不同。如果script標籤中使用了async屬性,JS指令碼載入過程不會阻塞文件解析,指令碼載入完成後會立即執行。如果sript標籤中使用了defer屬性,JS指令碼載入過程不會阻塞文件解析,當指令碼的執行要等得到文件解析完成之後。對於外部引用的指令碼檔案,從指令碼下載到指令碼執行完,文件解析過程會一直被阻塞

 

硬體加速

  WebKit渲染引擎的渲染方式分為軟體渲染和硬體渲染,這兩種渲染方式都可以分成兩個大的過程:一是得到網頁的繪製資訊;二是將網頁繪製資訊轉換成畫素並上屏

  得到網頁繪製資訊的過程需要遍歷RenderLayer樹,將RenderLayer樹包含的網頁繪製資訊先記錄下來,等到渲染時使用。記錄網頁繪製資訊這一步對渲染引擎而言,就是繪製的過程,渲染引擎本身並不知道繪製命令是否有被真正執行

【軟體渲染】

  軟體渲染的流程可概括為以下三步:

  1、從SurfaceFlinger獲得一塊圖形緩衝區

  2、在封裝這塊圖形緩衝區的SkCanvas上執行網頁繪製命令

  3、將繪製好的圖形緩衝區歸還SurfaceFlinger

  軟體渲染實現簡單,網頁內容直接繪製到一塊圖形緩衝區上,記憶體佔用更少。不足之處在於,由於網頁內容繪製在同一塊圖形緩衝區上,更新網頁內容時需要全部更新,無法區域性更新

【硬體渲染】

  相較於軟體渲染,硬體渲染實現比較複雜,網頁內容需要先繪製到一塊SkBitmap上,再通過圖形緩衝區上傳給GPU,需要更多記憶體

  硬體渲染是指網頁各層的合成是通過GPU完成的,它採用分塊渲染的策略,分塊渲染是指:網頁內容被一組Tile覆蓋,每塊Tile對應一個獨立的後端儲存,當網頁內容更新時,只更新內容有變化的Tile。分塊策略可以做到區域性更新,渲染效率更高

  硬體渲染的過程分為以下5步:

  1、在一塊封裝了SkBitmap的SkCanvas上執行一個Tile覆蓋的網頁資訊的繪製命令;

  2、將每個Tile對應的SkBitmap copy到從SurfaceFlinger獲得的一塊圖形緩衝區中;

  3、將所有Tile對應的圖形緩衝區上傳GPU進行合成;

  4、將合成好的網頁內容blit到Tile對應的與OnScreen FrameBuffer相關聯的Texture;

  5、通過GPU對Tile對應的Texture進行硬體繪製

  開啟硬體渲染,即合成加速,會為需要單獨繪製的每一層建立一個GraphicsLayer

  合成加速情況下,每一層網頁內容都對應一個後端儲存,這塊後端儲存由平臺實現,Android 4.2平臺提供的後端儲存是GraphicsLayerAndroid。開始記錄網頁繪製命令時,RenderLayerCompositor負責控制RenderLayer的遍歷,RenderLayer包含的繪製資訊最終記錄在其後端儲存上,即GraphicsLayerAndroid包含的PicturePile例項中

  一個RenderLayer物件如果需要後端儲存,它會建立一個RenderLayerBacking物件,該物件負責Renderlayer物件所需要的各種儲存。理想情況下,每個RenderLayer都可以建立自己的後端儲存,事實上不是所有RenderLayer都有自己的RenderLayerBacking物件。如果一個RenderLayer物件被像樣的建立後端儲存,那麼將該RenderLayer稱為合成層(Compositing Layer)

  哪些RenderLayer可以是合成層呢?如果一個RenderLayer物件具有以下的特徵之一,那麼它就是合成層:

  1、RenderLayer具有CSS 3D屬性或者CSS透視效果。

  2、RenderLayer包含的RenderObject節點表示的是使用硬體加速的視訊解碼技術的HTML5 ”video”元素。

  3、 RenderLayer包含的RenderObject節點表示的是使用硬體加速的Canvas2D元素或者WebGL技術。

  4、RenderLayer使用了CSS透明效果的動畫或者CSS變換的動畫。

  5、RenderLayer使用了硬體加速的CSSfilters技術。

  6、RenderLayer使用了剪裁(clip)或者反射(reflection)屬性,並且它的後代中包括了一個合成層。

  7、RenderLayer有一個Z座標比自己小的兄弟節點,該節點是一個合成層

  所以,進行硬體加速的渲染流程如下所示

browser

 

重繪迴流

  重繪和迴流是在頁面渲染過程中非常重要的兩個概念。頁面生成以後,指令碼操作、樣式表變更,以及使用者操作都可能觸發重繪和迴流

【迴流】

  迴流reflow是firefox裡的術語,在chrome中稱為重排relayout

  迴流是指視窗尺寸被修改、發生滾動操作,或者元素位置相關屬性被更新時會觸釋出局過程,在佈局過程中要計算所有元素的位置資訊。由於HTML使用的是流式佈局,如果頁面中的一個元素的尺寸發生了變化,則其後續的元素位置都要跟著發生變化,也就是重新進行流式佈局的過程,所以被稱之為迴流

  前面介紹過渲染引擎生成的3個樹:DOM樹、Render樹、Render Layer樹。迴流發生在Render樹上。常說的脫離文件流,就是指脫離渲染樹Render Tree

  觸發迴流包括如下操作:

  1、DOM元素的幾何屬性變化

  2、DOM樹的結構變化

  3、獲取下列屬性

offsetTop\offsetLeft\offsetWidth\offsetHeight\scrollTop\scrollLeft\scrollWidth\scrollHeight\clientTop\clientLeft\clientWidth\clientHeight\getComputedStyle()\currentStyle()

  4、改變元素的一些樣式

  5、調整瀏覽器視窗大小

  觸發迴流一定會觸發後續的重繪操作,而且對一個元素的迴流,可能會影響到父級元素。比如子元素浮動後,父元素會出現高度塌陷的情況。所以,效能優化的重點在於儘量只觸發小規模的重繪,儘量不觸發迴流

【重繪】

  重繪是指當與視覺相關的樣式屬性值被更新時會觸發繪製過程,在繪製過程中要重新計算元素的視覺資訊,使元素呈現新的外觀

  由於元素的重繪repaint只發生在渲染層 render layer上。所以,如果要改變元素的視覺屬性,最好讓該元素成為一個獨立的渲染層render layer

  下面以元素顯示為例,進行說明。實現元素顯示隱藏的方式有很多

  display: none/block,會引起迴流,從而引起重繪,效能較差

  visibility: visibile/hidden,只引起重繪,但由於沒有成為一個獨立的渲染層,會引起整個頁面(或當前渲染層)的重繪,效能較好

  opacity: 0/1,opacity小於1時,會產生render layer。所以opacity在0、1的變化中,引起了render layer的生成和銷燬,因此,也會引起迴流,從而引起重繪,效能較差。如果opacity: 0/0.9,則只會引起重繪

  如果對一個元素使用硬體加速渲染,如具有CSS 3D屬性,則不會進行重繪和迴流。但如果使用硬體渲染的元素過多,會造成GPU的傳輸壓力

【效能優化】

  下面列舉一些減少迴流次數的方法

  1、不要一條一條地修改DOM樣式,而是修改className或者修改style.cssText

  2、在記憶體中多次操作節點,完成後再新增到文件中去

  3、對於一個元素進行復雜的操作時,可以先隱藏它,操作完成後再顯示

  4、在需要經常獲取那些引起瀏覽器迴流的屬性值時,要快取到變數中

  5、不要使用table佈局,因為一個小改動可能會造成整個table重新佈局。而且table渲染通常要3倍於同等元素時間

  此外,將需要多次重繪的元素獨立為render layer渲染層,如設定absolute,可以減少重繪範圍;對於一些進行動畫的元素,可以進行硬體渲染,從而避免重繪和迴流

 

相關文章