天天看點

深入了解浏覽器工作原理

[1]組成

[2]核心

[3]渲染流程

[4]資源加載

[5]緩存

[6]網頁解析

[7]硬體加速

[8]重繪回流

前面的話

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

組成

  浏覽器的組成如下圖所示

深入了解浏覽器工作原理

  主要元件包括:

  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子產品

深入了解浏覽器工作原理

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

深入了解浏覽器工作原理

【Loader】

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

深入了解浏覽器工作原理

  注意:雖然大部分情況下不同資源可以并發下載下傳異步解析(如圖檔資源可以在首頁面解析顯示完成後再被顯示),但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

深入了解浏覽器工作原理

  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子產品加載頁面的内容,控制着浏覽器後續的解析以及繪制過程

深入了解浏覽器工作原理

  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規則表示成渲染引擎中相應規則類的執行個體的過程

深入了解浏覽器工作原理

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

  CSS檔案解析完成後,CSS規則都儲存在了CSSRuleList執行個體中,這些規則會在建立Render節點的過程中使用到。Node節點通過調用CSSStyleSelector執行個體的StyleForElement()函數為Render節點建立RenderStyle執行個體。有了RenderStyle執行個體才可以建立RenderObject執行個體。RenderStyle描述了RenderObject的排版布局資訊,也就是比對後的樣式資訊

深入了解浏覽器工作原理

  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坐标比自己小的兄弟節點,該節點是一個合成層

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

深入了解浏覽器工作原理

重繪回流

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

【回流】

  回流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,可以減少重繪範圍;對于一些進行動畫的元素,可以進行硬體渲染,進而避免重繪和回流

好的代碼像粥一樣,都是用時間熬出來的

深入了解浏覽器工作原理

繼續閱讀