網頁渲染機制

渲染引擎及關鍵渲染路徑(Critical Rendering Path)

通過網絡模塊加載到HTML文件后渲染引擎渲染流程如下,這也通常被稱作關鍵渲染路徑(Critical Rendering Path)

  • 構建DOM樹(DOM tree):從上到下解析HTML文檔生成DOM節點樹(DOM tree),也叫內容樹(content tree)
  • 構建CSSOM(CSS Object Model)樹:加載解析樣式生成CSSOM樹
  • 執行JavaScript:加載并執行JavaScript代碼(包括內聯代碼或外聯JavaScript文件)
  • 構建渲染樹(render tree):根據DOM樹和CSSOM樹,生成渲染樹(render tree)
  • 布局(layout):根據渲染樹將節點樹的每一個節點布局在屏幕上的正確位置
  • 繪制(painting):遍歷渲染樹繪制所有節點,為每一個節點適用對應的樣式,這一過程是通過UI后端模塊完成
DOM樹,CSSOM樹,渲染樹
關鍵渲染路徑

為了更友好的用戶體驗,瀏覽器會盡可能快的展現內容,而不會等到文檔所有內容到達才開始解析和構建/布局渲染樹,而是每次處理一部分,并展現在屏幕上,這也是為什么我們經常可以看到頁面加載的時候內容是從上到下一點一點展現的。

渲染引擎流程

Webkit渲染引擎流程如下圖:

Webkit

Gecko渲染引擎流程如下圖:

Gecko

如上圖,Webkit瀏覽器和Gecko瀏覽器渲染流程大致相同,不同的是:

  • Webkit瀏覽器中的渲染樹(render tree),在Gecko瀏覽器中對應的則是框架樹(frame tree),渲染對象(render object)對應的是框架(frame);
  • Webkit中的布局(Layout)過程,在Gecko中稱為回流(Reflow),本質是一樣的,后文會解釋回流的另一層含義–重新布局;
  • Gecko中HTML和DOM樹中間多了一層內容池(Content sink),可以理解成生成DOM元素的工廠。

單線程
渲染引擎是單線程工作的,意味著渲染流程是一步一步漸進完成的。

解析文檔(PARSER HTML)

在詳細介紹瀏覽器渲染文檔之前,先應該理解瀏覽器如何解析文檔:解析文檔的順序,對于CSS和JavaScript如何處理等。

  • 解析順序
    瀏覽器按從上到下的順序掃描解析文檔;

  • 解析樣式和腳本

    • 腳本

      由于通常會在JavaScript腳本中改變文檔DOM結構,于是瀏覽器以同步方式解析,加載和執行腳本,瀏覽器在解析文檔時,當解析到<script>標簽時,會解析其中的腳本(對于外鏈的JavaScript文件,需要先加載該文件內容,再進行解析),然后立即執行,這整個過程都會阻塞文檔解析,直到腳本執行完才會繼續解析文檔。就是說由于腳本是同步加載和執行的,它會阻塞文檔解析,這也解釋了為什么現在通常建議將<script>標簽放在</body>標簽前面,而不是放在<head>標簽里。現在HTML5提供defer和async兩個屬性支持延遲和異步加載JavaScript文件,如:

<script defer src="script.js">  
  • 改進

針對上文說的腳本阻塞文檔解析,主流瀏覽器如Chrome和FireFox等都有一些優化,比如在執行腳本時,開啟另一個線程解析剩余的文檔以找出并加載其他的待下載外部資源(不改變主線程的DOM樹,僅優化加載外部資源)。

  • 樣式

不同于腳本,瀏覽器對樣式的處理并不會阻塞文檔解析,大概是因為樣式表并不會改變DOM結構。

  • 樣式表與腳本

你可能想問樣式是否會阻塞腳本文件的加載執行呢?正常情況是不會的,但是存在一個問題是通常我們會在腳本中請求樣式信息,但是在文檔解析時,如果樣式尚未加載或解析,將會得到錯誤信息,對于這一問題,FireFox瀏覽器和Webkit瀏覽器處理策略不同:
- 當存在有樣式文件未被加載和解析時,FireFox瀏覽器會阻塞所有腳本;
- 而Webkit瀏覽器只會阻塞操作了改文件內聲明的樣式屬性的腳本。

構建DOM樹

DOM樹,即文檔內所有節點構成的一個樹形結構。
假設瀏覽器獲取返回的如下HTML文檔:

<!doctype html>
    <html>
    <head>
      <link rel="stylesheet" href="./theme.css"></link>
      <script src="./config.js"></script>
      <title>關鍵渲染路徑</title>
    </head>
    <body>
      <h1 class="title">關鍵渲染路徑</h1>
      <p>關鍵渲染路徑介紹</p>
      <footer>@copyright2017</footer>
    </body>
    </html>

首先瀏覽器從上到下依次解析文檔構建DOM樹,如下:

DOM 樹

構建CSSOM樹

CSSOM樹,與DOM樹結構相似,只是另外為每一個節點關聯了樣式信息。

theme.css樣式內容如下:

 html, body {
    width: 100%;
    height: 100%;
    background-color: #fcfcfc;
    }
    .title {
    font-size: 20px;
    }
    .footer {
    font-size: 12px;
    color: #aaa;
    }

構建CSSOM樹如圖:

CSSOM樹

執行JAVASCRIPT

上文已經闡述了文檔解析時對腳本的處理,我們得知腳本加載,解析和執行會阻塞文檔解析,而在特殊情況下樣式的加載和解析也會阻塞腳本,所以現在推薦的實踐是<script>標簽放在</body>標簽前面。

構建渲染樹(RENDER TREE)

DOM樹和CSSOM樹都構建完了,接著瀏覽器會構建渲染樹:

渲染樹,代表一個文檔的視覺展示,瀏覽器通過它將文檔內容繪制在瀏覽器窗口,展示給用戶,它由按順序展示在屏幕上的一系列矩形對象組成,這些矩形對象都帶有字體,顏色和尺寸,位置等視覺樣式屬性。對于這些矩對象,FireFox稱之為框架(frame),Webkit瀏覽器稱之為渲染對象(render object, renderer),后文統稱為渲染對象。

這里把渲染樹節點稱為矩形對象,是因為,每一個渲染對象都代表著其對應DOM節點的CSS盒子,該盒子包含了尺寸,位置等幾何信息,同時它指向一個樣式對象包含其他視覺樣式信息。

渲染樹與DOM樹

每一個渲染對象都對應著DOM節點,但是非視覺(隱藏,不占位)DOM元素不會插入渲染樹,如<head>
元素或聲明display: none;
的元素,渲染對象與DOM節點不是簡單的一對一的關系,一個DOM可以對應一個渲染對象,但一個DOM元素也可能對應多個渲染對象,因為有很多元素不止包含一個CSS盒子,如當文本被折行時,會產生多個行盒,這些行會生成多個渲染對象;又如行內元素同時包含塊元素和行內元素,則會創建一個匿名塊級盒包含內部行內元素,此時一個DOM對應多個矩形對象(渲染對象)。
渲染樹及其對應DOM樹如圖:

image.png
  • 圖中渲染樹viewport即視口,是文檔的初始包含塊,scroll代表滾動區域
  • 渲染樹并不會包含顯式或隱式地display:none;的標簽元素。

布局過程

布局是一個從上到下,從外到內進行的遞歸過程,從根渲染對象,即對應著HTML文檔根元素<html>,然后下一級渲染對象,如對應著<body>元素,如此層層遞歸,依次計算每一個渲染對象的幾何信息(位置和尺寸)。

幾何信息-位置和尺寸,即相對于窗口的坐標和尺寸,如根渲染對象,其坐標為(0, 0),尺寸即是視口
尺寸(瀏覽器窗口的可視區域)。

每一個渲染對象的布局流程基本如:

  1. 計算此渲染對象的寬度(width);
  2. 遍歷此渲染對象的所有子級,依次:
    2.1 設置子級渲染對象的坐標
    2.2 判斷是否需要觸發子渲染對象的布局或回流方法,計算子渲染對象的高度(height)
  3. 設置此渲染對象的高度:根據子渲染對象的累積高,margin和padding的高度設置其高度;
  4. 設置此渲染對象臟位值為false。

繪制(PAINTING)

最后是繪制(paint)階段或重繪(repaint)階段,瀏覽器UI組件將遍歷渲染樹并調用渲染對象的繪制(paint)方法,將內容展現在屏幕上,也有可能在之后對DOM進行修改,需要重新繪制渲染對象,也就是重繪,繪制和重繪的關系可以參考布局和回流的關系。


一個重要的概念reflow和repaint

  • Repaint

屏幕的一部分要重畫,比如某個CSS的背景色變了。但是元素的幾何尺寸沒有變。

  • Reflow

意味著元件的幾何尺寸變了,我們需要重新驗證并計算Render Tree。是Render Tree的一部分或全部發生了變化。這就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的幾何尺寸發生了變化,需要重新布局,也就叫reflow)reflow 會從<html>這個root frame開始遞歸往下,依次計算所有的結點幾何尺寸和位置,在reflow過程中,可能會增加一些frame,比如一個文本字符串必需被包裝起來。

Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每個結點都會有reflow方法,一個結點的reflow很有可能導致子結點,甚至父點以及同級結點的reflow。在一些高性能的電腦上也許還沒什么,但是如果reflow發生在手機上,那么這個過程是非常痛苦和耗電的。
所以,下面這些動作有很大可能會是成本比較高的。

  • 當你增加、刪除、修改DOM結點時,會導致Reflow或Repaint
  • 當你移動DOM的位置,或是搞個動畫的時候。
  • 當你修改CSS樣式的時候。
  • 當你Resize窗口的時候(移動端沒有這個問題),或是滾動的時候。
  • 當你修改網頁的默認字體時。
    注:display:none會觸發reflow,而visibility:hidden只會觸發repaint,因為沒有發現位置變化。

減少reflow/repaint

  • 不要一條一條地修改DOM的樣式。與其這樣,還不如預先定義好css的class,然后修改DOM的className。
  • 不要把DOM結點的屬性值放在一個循環里當成循環里的變量。不然這會導致大量地讀寫這個結點的屬性。
  • 盡可能的修改層級比較低的DOM。當然,改變層級比較低的DOM有可能會造成大面積的reflow,但是也可能影響范圍很小。
  • 為動畫的HTML元件使用fixed或absoult的position,那么修改他們的CSS是不會reflow的。
  • 千萬不要使用table布局。因為可能很小的一個小改動會造成整個table的重新布局。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 瀏覽器基本結構 瀏覽器結構一般包括如下:1.用戶界面(User Interface):用戶所看到的界面,并且與之交...
    losspm閱讀 345評論 0 0
  • 1. 介紹 瀏覽器可能是最廣泛使用的軟件。本書將介紹瀏覽器的工作原理。我們將看到,當你在地址欄中輸入google....
    康斌閱讀 2,064評論 7 18
  • 簡介瀏覽器可以被認為是使用最廣泛的軟件,本文將介紹瀏覽器的工 作原理,我們將看到,從你在地址欄輸入google.c...
    聽風閣閱讀 3,316評論 0 7
  • 轉載說明 一、介紹 瀏覽器可以被認為是使用最廣泛的軟件,本文將介紹瀏覽器的工作原理,我們將看到,從你在地址欄輸入g...
    17碎那年閱讀 2,471評論 0 22
  • 心智模型(Mental Model) 人們由經驗及學習,對自己、他人、環境以及接觸到的事物形成的模型,腦海中對某些...
    Meowmaid閱讀 1,655評論 0 1