瀏覽器解析渲染HTML文檔的過程

參考資料

  1. 瀏覽器的工做原理
  2. 瀏覽器加載網頁時的過程是什麼?
  3. HTML渲染過程詳解

瀏覽器的工做原理

1、瀏覽器的高層結構

瀏覽器的主要組件爲:javascript

  1. 用戶界面 - 包括地址欄、前進/後退按鈕、書籤菜單等。除了瀏覽器主窗口顯示的您請求的頁面外,其餘顯示的各個部分都屬於用戶界面。
  2. 瀏覽器引擎 - 在用戶界面和呈現引擎之間傳送指令。
  3. 呈現引擎 - 負責顯示請求的內容。若是請求的內容是 HTML,它就負責解析 HTML 和 CSS 內容,並將解析後的內容顯示在屏幕上。
  4. 網絡 - 用於網絡調用,好比 HTTP 請求。其接口與平臺無關,併爲全部平臺提供底層實現。
  5. 用戶界面後端 - 用於繪製基本的窗口小部件,好比組合框和窗口。其公開了與平臺無關的通用接口,而在底層使用操做系統的用戶界面方法。
  6. JavaScript 解釋器。用於解析和執行 JavaScript 代碼。
  7. 數據存儲。這是持久層。瀏覽器須要在硬盤上保存各類數據,例如 Cookie。新的 HTML 規範 (HTML5) 定義了「網絡數據庫」,這是一個完整(可是輕便)的瀏覽器內數據庫。

    layers.png-37kB

值得注意的是,和大多數瀏覽器不一樣,Chrome 瀏覽器的每一個標籤頁都分別對應一個呈現引擎實例。每一個標籤頁都是一個獨立的進程。css

2、主流程

呈現引擎一開始會從網絡層獲取請求文檔的內容,內容的大小通常限制在 8000 個塊之內。html

而後進行以下所示的基本流程:html5

flow.png-13.9kB

呈現引擎將開始解析 HTML 文檔,並將各標記逐個轉化成「內容樹」上的 DOM 節點。同時也會解析外部 CSS 文件以及樣式元素中的樣式數據。HTML 中這些帶有視覺指令的樣式信息將用於建立另外一個樹結構:呈現樹。java

呈現樹包含多個帶有視覺屬性(如顏色和尺寸)的矩形。這些矩形的排列順序就是它們將在屏幕上顯示的順序。jquery

呈現樹構建完畢以後,進入「佈局」處理階段,也就是爲每一個節點分配一個應出如今屏幕上的確切座標。下一個階段是繪製 - 呈現引擎會遍歷呈現樹,由用戶界面後端層將每一個節點繪製出來。web

須要着重指出的是,這是一個漸進的過程。爲達到更好的用戶體驗,呈現引擎會力求儘快將內容顯示在屏幕上。它沒必要等到整個 HTML 文檔解析完畢以後,就會開始構建呈現樹和設置佈局。在不斷接收和處理來自網絡的其他內容的同時,呈現引擎會將部份內容解析並顯示出來。chrome

主流程示例:
webkitflow.png-34.7kB數據庫

3、處理腳本和樣式表的順序

  1. 腳本
    網絡的模型是同步的。網頁做者但願解析器遇到 <script> 標記時當即解析並執行腳本。文檔的解析將中止,直到腳本執行完畢。若是腳本是外部的,那麼解析過程會中止,直到從網絡同步抓取資源完成後再繼續。此模型已經使用了多年,也在 HTML4 和 HTML5 規範中進行了指定。做者也能夠將腳本標註爲「defer」,這樣它就不會中止文檔解析,而是等到解析結束才執行。HTML5 增長了一個選項,可將腳本標記爲異步,以便由其餘線程解析和執行。
  2. 預解析
    WebKit 和 Firefox 都進行了這項優化。在執行腳本時,其餘線程會解析文檔的其他部分,找出並加載須要經過網絡加載的其餘資源。經過這種方式,資源能夠在並行鏈接上加載,從而提升整體速度。請注意,預解析器不會修改 DOM 樹,而是將這項工做交由主解析器處理;預解析器只會解析外部資源(例如外部腳本、樣式表和圖片)的引用。
  3. 樣式表
    另外一方面,樣式表有着不一樣的模型。理論上來講,應用樣式表不會更改 DOM 樹,所以彷佛沒有必要等待樣式表並中止文檔解析。但這涉及到一個問題,就是腳本在文檔解析階段會請求樣式信息。若是當時尚未加載和解析樣式,腳本就會得到錯誤的回覆,這樣顯然會產生不少問題。這看上去是一個非典型案例,但事實上很是廣泛。Firefox 在樣式表加載和解析的過程當中,會禁止全部腳本。而對於 WebKit 而言,僅當腳本嘗試訪問的樣式屬性可能受還沒有加載的樣式表影響時,它纔會禁止該腳本。
  4. 呈現樹構建
    在 DOM 樹構建的同時,瀏覽器還會構建另外一個樹結構:呈現樹。這是由可視化元素按照其顯示順序而組成的樹,也是文檔的可視化表示。它的做用是讓您按照正確的順序繪製內容。

Firefox 將呈現樹中的元素稱爲「框架」。WebKit 使用的術語是呈現器或呈現對象。
呈現器知道如何佈局並將自身及其子元素繪製出來。bootstrap

4、佈局

呈現器在建立完成並添加到呈現樹時,並不包含位置和大小信息。計算這些值的過程稱爲佈局或重排。

HTML 採用基於流的佈局模型,這意味着大多數狀況下只要一次遍歷就能計算出幾何信息。處於流中靠後位置元素一般不會影響靠前位置元素的幾何特徵,所以佈局能夠按從左至右、從上至下的順序遍歷文檔。可是也有例外狀況,好比 HTML 表格的計算就須要不止一次的遍歷。

座標系是相對於根框架而創建的,使用的是上座標和左座標。

佈局是一個遞歸的過程。它從根呈現器(對應於 HTML 文檔的 <html> 元素)開始,而後遞歸遍歷部分或全部的框架層次結構,爲每個須要計算的呈現器計算幾何信息。

根呈現器的位置左邊是 0,0,其尺寸爲視口(也就是瀏覽器窗口的可見區域)。
全部的呈現器都有一個「layout」或者「reflow」方法,每個呈現器都會調用其須要進行佈局的子代的 layout 方法。

5、繪製

在繪製階段,系統會遍歷呈現樹,並調用呈現器的「paint」方法,將呈現器的內容顯示在屏幕上。繪製工做是使用用戶界面基礎組件完成的。

我的理解總結

1、解析器與預解析機制

呈現引擎從網絡層獲取請求文檔的內容,而後開始解析 HTML 文檔,並將各標記逐個轉化爲 DOM樹(內容樹)上的 DOM 節點,同時也會解析外部 CSS 文件以及樣式元素中的樣式數據。HTML 中這些帶有視覺指令的樣式信息將用於建立另外一個樹結構:渲染樹(呈現樹)。呈現樹構建完畢以後,呈現引擎將對呈現樹進行佈局和繪製。

呈現引擎 的解析包括 HTML 解析和 CSS 解析,HTML 解析器的輸出「解析樹」是由 DOM 元素和屬性節點構成的樹結構,DOM 是文檔對象模型 (Document Object Model) 的縮寫。它是 HTML 文檔的對象表示,同時也是外部內容(例如 JavaScript)與 HTML 元素之間的接口。解析樹的根節點是「Document」對象。CSS 解析器會將 CSS 樣式文件和樣式元素中的樣式數據解析爲 CSS 規則樹,瀏覽器結合 CSS 規則樹和 DOM 樹生成渲染樹。

JavaScript 解釋器 用於解析和執行 JavaScript 代碼。

通常來說,咱們認爲瀏覽器從網絡層接收到 HTML 文檔內容,而後開始解析文檔生成 DOM 樹,遇到 CSS 樣式表標籤或 JS 腳本標籤就起新線程去下載它們,並繼續構建 DOM 樹,瀏覽器根據 DOM 樹構建渲染樹,最後瀏覽器將渲染書繪製到用戶界面。

在上述描述中,須要着重指出的是,HTML 文檔的解析和渲染是一個漸進的過程。爲達到更好的用戶體驗,呈現引擎會力求儘快將內容顯示在屏幕上。它沒必要等到整個 HTML 文檔解析完畢,就會開始構建呈現樹和設置佈局。在不斷接收和處理來自網絡的其他內容的同時,呈現引擎會將部份內容解析並顯示出來。

瀏覽器的預解析。WebKit 和 Firefox 都進行了這項優化。在執行腳本時,其餘線程會解析 HTML 文檔的其他部分,找出並加載須要經過網絡加載的其餘資源。經過這種方式,資源能夠在並行鏈接上加載,從而提升整體速度。請注意,預解析器不會修改 DOM 樹,而是將這項工做交由主解析器處理;預解析器只會解析外部資源(例如外部腳本、樣式表和圖片)的引用。

瀏覽器的預解析能夠減緩渲染被阻塞的狀況,例如文檔解析過程當中預加載器發現了 <script src="last.js"></script> 標籤,會對 last.js 文件進行加載並放在瀏覽器緩存中,這樣當解析器遇到這個 <script> 標記時,因爲預加載器已經將 last.js 文件加載下來了,因此 last.js 會被當即執行,不須要等待從網絡抓取資源,減緩了對渲染的阻塞。

2、CSS 和 JS 的處理順序和阻塞分析

HTML 文檔的解析和渲染過程當中,外部樣式表和腳本 順序執行、併發加載

JS 腳本會阻塞 HTML 文檔的解析,包括 DOM 樹的構建和渲染樹的構建;CSS 樣式表會阻塞渲染樹的構建,但 DOM 樹依然繼續構建(除非遇到 script 標籤且 css 文件此時仍未加載完成),但不會渲染繪製到頁面上。

在 HTML 文檔的解析過程當中,解析器遇到 <script> 標記時會當即解析並執行腳本,HTML 文檔的解析將被阻塞,直到腳本執行完畢。若是腳本是外部的,那麼解析過程會中止,直到從網絡抓取資源並解析和執行完成後,再繼續解析後續內容。

理論上來講,應用樣式表不會更改 DOM 樹,所以彷佛沒有必要等待樣式表並中止文檔解析。但這涉及到一個問題,就是腳本在文檔解析階段會請求樣式信息。若是當時尚未加載和解析樣式,腳本就會得到錯誤的回覆,這樣顯然會產生不少問題。這看上去是一個非典型案例,但事實上很是廣泛。Firefox 在樣式表加載和解析的過程當中,會禁止全部腳本。而對於 WebKit 而言,僅當腳本嘗試訪問的樣式屬性可能受還沒有加載的樣式表影響時,它纔會禁止該腳本。

但不管是哪一種狀況致使的阻塞,該加載的外部資源仍是會加載,例如外部腳本、樣式表和圖片。HTML 文檔的解析可能會被阻塞,但外部資源的加載不會被阻塞。

CSS 外部樣式表的加載會阻塞外部腳本的執行,但並不會阻塞外部腳本的加載。這一點能夠經過 chrome 調試工具中的 Network - Waterfall 進行驗證,可是須要注意 chrome 的併發鏈接數(同一域名)上限爲 6 個。

代碼

併發加載-1.png-101kB

由上面兩張截圖能夠看到,jquery.min.js 腳本文件與 bootstrap.css 等樣式文件並行加載,可是因爲 chrome 的併發鏈接數上限爲 6 個,所以 bootstrap.min.js 腳本、xxx.css 樣式等文件的加載會等待前面的文件加載完成,有可用鏈接數的時候纔開始加載。

併發加載-2.png-100.7kB

瞭解以上信息以後,咱們能夠對該頁面進行相應優化,例如對CSS文件進行壓縮處理、使用 CDN,將資源分佈在多個域名下、合併 CSS 文件,減小 HTTP 請求數量等,來提升 CSS 的加載速度,減小 HTML 文檔解析和渲染的阻塞時間。

browser only allows six TCP connections per origin on HTTP 1.

瀏覽器的併發請求數目限制是針對同一域名的。所以可使用 CDN 加速技術來提升用戶訪問網站的響應速度,這樣使用了 CDN 的資源加載不會佔用當前域名下的併發鏈接數,從而減小阻塞的時間。

網頁性能

瞭解 HTML 文檔的解析和渲染的過程對於分析網頁性能有着重要意義,它能夠幫助咱們找到影響網頁性能的關鍵因素。例如,咱們知道 JS 外部腳本的執行會阻塞文檔的解析,那麼重量級的第三方插件則會影響首頁加載的速度,若是所以影響到了用戶體驗,咱們就須要考慮這個第三方插件的使用成本是否是過高了,可否使用其餘輕量級的插件進行替代,或者只使用其中一部分模塊。

以 Datatables 爲例:

network.PNG-106.1kB

上圖是一個項目頁面的 Network 截圖,紅色框中的部分出現了約 700ms 左右的空白,咱們須要知道爲何頁面的加載會出現這樣的狀況,這段空白時間瀏覽器在幹什麼?

咱們分析 Timeline 圖,看看瀏覽器在這段時間的具體信息,以下:

timeline.PNG-136.9kB

經過 Timeline 圖咱們能夠看到,在 250ms~900ms 時間區間內,瀏覽器在執行 datatables.min.js 腳本代碼,這個腳本的執行阻塞了文檔的解析,耗時約 700ms,對應了 Network 圖中的空白。

咱們繼續查看頁面總的耗時時間,評估 700ms 耗時的影響,以下:

performance.PNG-6.2kB

能夠看到,頁面總的完成耗時爲 1.66s,由此可知 datatables.min.js 的執行耗時佔了很大比重,應當慎重考慮是否必定要使用這個插件,可否使用其餘輕量級的插件進行替代,或者可否精簡插件的沒必要要模塊,或者捨棄插件的使用。

參考資料-1
瀏覽器接收到html代碼,多是一份完整的文檔,也多是一個chunk,即開始解析。解析過程是先構建dom樹,再根據dom樹構建渲染樹,最後瀏覽器將渲染樹繪製到頁面上。
構建dom樹的過程即根據html代碼自上而下進行構建,當遇到script文件加載/執行會阻塞後面dom樹的構建(javascript可能會改變dom樹),而遇到css文件則會阻塞渲染樹的構建,即dom樹依然繼續構建(除非遇到script標籤而且css文件依舊未加載完成),但不會渲染繪製到頁面上。而不管哪一個阻塞,該加載的文件仍是會加載,例如html文檔中的其餘css/js/圖片文件。
另外javascript被加載後就會被執行,執行的過程也阻塞樹的構建。是執行完了才解析其餘內容,而不是執行完了才加載其餘內容。

做者:加冰
連接:https://www.zhihu.com/questio...

參考資料-2
首先,開源瀏覽器通常以8k每塊下載html頁面。
而後解析頁面生成DOM樹,遇到css標籤或JS腳本標籤就新起線程去下載他們,並繼續構建DOM。
下載完後解析CSS爲CSS規則樹,瀏覽器結合CSS規則樹和DOM樹生成Render Tree。
注意:構建CSS Object Model(CSSOM)會阻塞JavaScript的執行。JavaScript的執行也會阻塞DOM的構建。
JavaScript下載後能夠經過DOM API修改DOM,經過CSSOM API修改樣式做用域Render Tree。
每次修改會形成Render Tree的從新佈局和重繪。只要修改DOM或修改了元素的形狀或大小,就會觸發Reflow,單純修改元素的顏色只需Repaint一下(調用操做系統Native GUI的API繪製)。

做者:陳金
連接:https://www.zhihu.com/questio...

相關文章
相關標籤/搜索