史上最詳細的頁面渲染機制

頁面的渲染過程

當咱們在瀏覽器裏輸入一個 URL 後,最終會呈現一個完整的網頁。會經歷如下幾個步驟:javascript

一、HTML 的加載

頁面上輸入 URL 後,會先拿到 HTML 文件。HTML是一個頁面的基礎,因此會在最開始的時候下載它,下載完畢後就開始對它進行解析css

二、其餘靜態資源的下載

HTML 在解析的過程當中,若是發現 HTML 文本里面有一些外部的資源連接,好比 CSS、JS 和圖片等,會當即啓用別的線程下載這些靜態資源。在 head 中遇到 JS 文件時,HTML 的解析會停 下來,等 JS 文件下載結束而且執行完,HTML 的解析工做再接着來,防止 JS 修改已經完成的解析結果html

知識拓展

由上得知,JS 文件放在 head 中屬於同步加載,會阻塞 DOM 樹的構建,進而影響頁面的加載。當 JS 文件較多時,頁面白屏的時間也會變長java

那如何避免這個問題呢?下面有三個解決方案瀏覽器

一、 script 放在 body 裏(通常是 </body> 上面)

因爲 DOM 是自上而下解析的,所以 JS 不會阻塞 DOM 的解析,並且這時候能夠在 JS 中操做 DOM數據結構

二、設置 defer 屬性

經過給 script 標籤設置 defer 屬性,將腳本文件設置爲延遲加載,當瀏覽器遇到帶有 defer 屬性的 script 標籤時,會再開啓一個線程去下載 JS 文件,同時繼續解析 HTML,等 HTML 所有解析完、DOM 加載完成以後,再去執行加載好的 JS 文件。只適用於引用外部 JS 文件,而且能夠確保全部加了 defer 屬性的腳本會按順序執行async

三、設置 async 屬性

async 屬性和 defer 屬性相似,也是會開啓一個線程去下載js文件,但和 defer 不一樣的是,aysnc 會在 JS 加載完成後馬上執行,而不是會等到 DOM 加載完成以後再執行,因此仍是有可能會形成阻塞。一樣的也是隻適用於外部 JS 文件,若是有多個設置了 aysnc 的 JS 文件,不能像 defer 那樣保證按照順序執行佈局

四、動態建立腳本

這樣去動態建立文件也不會影響到頁面的加載,如下是百度統計的demo性能

<script>
 var _hmt = _hmt || [];
(function () {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js";
  var s = document.getElementsByTagName("script")[0];
  s.parentNode.insertBefore(hm, s);
})();
</script>
複製代碼

三、DOM 樹的構建

DOM 的全稱是:文檔對象模型(Document Object Model),在 HTML 解析時,解析器會把解析完的 HTML轉化成 DOM 對象,再進一步構建 DOM 樹優化

四、CSSOM 樹的構建

當 CSS 下載完,CSS 解析器就開始對 CSS 進行解析,把 CSS 解析成 CSS 對象,而後把這些 CSS 對象組裝成一顆 CSSOM 樹

五、渲染樹的構建

DOM 樹和 CSSOM 樹都構建完成之後,瀏覽器會根據這兩棵樹構建出一棵渲染樹

六、佈局計算

渲染樹構建完成之後,全部元素的位置關係和須要應用的樣式就肯定了。這時候瀏覽器會計算出全部元素的大小 和絕對位置

七、渲染

佈局計算完成之後,瀏覽器就能夠在頁面上渲染元素了,通過渲染引擎的處理後,整個頁面就顯示在了屏幕上

上面是一個瀏覽器渲染頁面的大概過程,接下來就來把這個過程的幾個重要部分再詳細講下


DOM 樹的構建

頁面中的每個 HTML 標籤,都會被瀏覽器解析成一個對象,咱們稱它爲文檔對象(Document Object)。HTML 本質是一個嵌套結構,在解析的時候會把每一個文檔對象用一個樹形結構組織起來,全部的文檔對象都會掛在一個叫作 Document 的東西上,這種組織方式就是 HTML 最基礎的結構–文檔對象模型(DOM),這棵樹裏面的每一個文檔對象就叫作 DOM 節點。 在 HTML 加載的過程當中,DOM 樹就在開始構建了。構建的過程是先把 HTML 裏每一個標籤都解析成 DOM 節點(每 個標籤的屬性、值和上下文關係等都在這個文檔對象裏),而後使用深度遍歷的方法把這些對象構形成一棵樹。 咱們如下面的 HTML 文件爲例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <header>
        <p>header</p>
    </header>
    <div class="content">
        <p class="title">title</p>
    </div>
</body>
</html>
複製代碼

在構建 DOM 樹的時候,就是從最外層 HTML 節點開始,按深度優先的方式構建。之因此用深度優先,是由於HTML在加載的時候是自上而下的,最早加載的是根節點 ,而後是根節點的第一個子節點 ,再而後是 head 的第一個子節點,head 構建完成後再去構建 body 部分的內容,以此類推。使用深度優先的方式構建 這棵樹就和文檔的加載順序吻合了。最後,上面這個 HTML 結構會生成這樣一顆 DOM 樹:

image.png

CSSOM 樹的構建

在瀏覽器構建 DOM 樹的同時,若是樣式也加載完成了,那麼 CSSOM 樹也在同步地構建。CSS 樹和 DOM 類 似,它的樹形結構會記錄全部樣式的信息

假設咱們給上面的 HTML 加上這些樣式:

body {
  font-size: 16px;
}

p {
  margin: 0;
  padding: 0;
}

header {
  width: 100%;
  height: 50px;
  text-align: center;
  display: none;
}

header p {
  color: #333;
}

.content {
  padding: 0 16px;
}

.content .title {
  font-size: 24px;
}
複製代碼

這組樣式經過解析器的構造,會獲得相似這樣的一顆 CSSOM 樹:

image.png

Tips:

一、這棵樹的結構只是一個示意圖,並非瀏覽器構造 CSSOM 樹的真實數據結構,各個瀏覽器實現 CSSOM 樹的方式也不徹底相同

二、CSSOM 樹和 DOM 樹是獨立的兩個數據結構,它們沒有一一對應的關係。DOM 樹描述的是 HTML 標籤的層級關係,CSSOM 樹描述的是選擇器之間的層級關係

三、CSS 中存在樣式繼承機制,有些屬性在父節點設置後,其後代節點都會具有這個樣式。好比在 html 上設置一個 "color: red" ,那頁面的全部標籤都會繼承這個屬性。固然不是全部標籤和屬性都是能夠繼承的,好比 border 這種屬性就是不可繼承的。若是 border 可繼承,那麼在一個父元素裏設置了之後,全部子元素都會有個邊框,這顯然是不合理的。因此在大部分狀況下,經過這種推理,就能知道哪些樣式能夠繼承,哪些樣式不能夠繼承

小結

img
這一篇就講了大概這麼些東西,下一篇接着講渲染樹的構建、渲染過程、佈局、繪製、重排重繪以及怎麼優化頁面性能(還沒寫完,只有個大綱)

以爲寫的很差或不正確的地方還請不吝賜教 [抱拳]

相關文章
相關標籤/搜索