深入理解瀏覽器工作原理


前面的話

  瀏覽器(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,可以減少重繪范圍;對於一些進行動畫的元素,可以進行硬件渲染,從而避免重繪和回流

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM