【譯】瀏覽器如何呈現頁面(二)

這篇文章是第二篇,第一篇在這裏。javascript

第一篇大體講到了瀏覽器從獲取原始數據開始,直到把內容畫到屏幕上,可是尚未完。css

以後,還有第三篇(整理)在這裏html

渲染阻塞

當你聽到「渲染阻塞」的時候,你會想到啥?我猜是,‘某些行爲阻止了瀏覽器把內容畫到屏幕上’。java

的確是你猜的那樣。瀏覽器

因此這裏有了咱們的第一個優化點,把最重要的HTML內容和CSS樣式提供給客戶端,越快越好。服務器

DOM和CSSOM都必須在屏幕繪製以前構造完成,因此HTMLCSS都是渲染阻塞資源。網絡

因此關鍵點就是你應該讓客戶端竟可能快地獲取你的htmlcss,這樣就可以優化首屏渲染的時間。app

JavaScript登場

如今,基本只要是個網站都有JavaScript。。。async

JavaScript是會修改頁面內容以及樣式的。做爲實現,你能用JS從DOM樹裏添加刪除元素,還可以修改元素的CSSOM的屬性。post

這很好,可是這也須要付出代價。

考慮一下下面的HTML文檔:

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>
</body>

</html>
複製代碼

這是個很簡單的例子,style.css內容也很簡單,以下:

body {
  background: #8cacea;
}
複製代碼

最終顯示結果以下:

結果

簡單文本和圖片渲染到了屏幕上。

從以前的步驟來看,通過 原始數據->字符->標記 轉換後,瀏覽器一旦讀到<link rel="stylesheet" href="style.css">這一行的時候,就回去請求這個css文件style.css,DOM的構建仍然繼續,而且一旦css文件返回內容以後,這個CSSOM的構建也開始了。

當JavaScript來了以後,這個過程會發生什麼樣的變化呢?

記住一點,只要瀏覽器讀到了script標籤,那DOM的構建就會暫停!

整棵DOM樹的建立過程會暫停,直到這個script運行完成。(這裏不是指那些ready以後的腳本哈。)

重要1

由於js會去修改DOM和CSSOM,而瀏覽器又不可以肯定這個js會作些什麼,因此須要經過暫停整個DOM的建立來預防。

這樣會有多糟?

讓咱們用上面那個例子,而後加一點基礎的script進去:

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>

    <script> let header = document.getElementById("header"); console.log("header is: ", header); </script>
</body>

</html>
複製代碼

在這個script裏,我用id去Dom裏獲得一個節點header,而後打印到了console。

結果1

你有注意到這個script是在body的底部麼,讓咱們看下若是把它放在頭部會怎樣:

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
    <script> let header = document.getElementById("header"); console.log("header is: ", header); </script>
</head>

<body>

    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>
</body>

</html>
複製代碼

運行後,獲得的結果header是null

結果2

爲何?

其實很簡單。當HTML解析器正在建立DOM樹的時候,發現有一個script標籤,而此時,body標籤以及它的內容尚未解析出來。DOM構建被暫停直到這個script執行完。

這裏又帶出了另外一個重要的點。

你腳本的位置很重要。

重要2

若是你以文件形式加載一個js,同樣也會暫停DOM的建立,相似:<script src="app.js"></script> 若是這個app.js放在遠程服務器上,而且網絡很慢,須要加載3秒呢?那麼,DOM的構建就要等到3秒以後再繼續!!!

這是個很大的性能問題,但還不是所有。

記得JS還可以改變CSSOM,好比:

document.getElementsByTagName("body")[0].style.backgroundColor = "red";

那麼,在CSSOM建立好以前,解析器讀到script會怎樣呢?

結果是js的執行將會暫停,直到CSSOM建立好。

重要3

因此當遇到script的時候,DOM的構建會中止,可是CSSOM並不會(【譯註】不但不會,還會暫停js的執行)。

【譯註】這裏我想要補充一下,經過上面所說的,咱們能夠推出一個觀點:

JS 文件不僅是阻塞 DOM 的構建,它會致使 CSSOM 也阻塞 DOM 的構建。

本來 DOM 和 CSSOM 的構建是互不影響,井水不犯河水,可是一旦引入了 JavaScript,CSSOM 也開始阻塞 DOM 的構建。由於不完整的 CSSOM 是沒法使用的,若是 JavaScript 想訪問 CSSOM 並更改它,那麼在執行 JavaScript 時,必需要能拿到完整的 CSSOM。因此就致使了一個現象,若是瀏覽器還沒有完成 CSSOM 的下載和構建,而咱們卻想在此時運行腳本,那麼瀏覽器將延遲腳本執行和 DOM 構建,直到完成 CSSOM 的建立。也就是說,在這種狀況下,瀏覽器會先去構建 CSSOM,而後再執行 JavaScript,最後在繼續構建 DOM。

async屬性

默認狀況下,每一個script都會組織DOM樹的建立。不夠有一個方法可以改變這一個行爲。

若是你在script標籤里加一個async屬性,那麼DOM樹的建立就不會暫停,這個腳本就會在下載完後去執行。例如:

<script src="https://some-link-to-app.js" async></script>

關鍵渲染路徑

咱們以前所說的從獲取html,css和js原始數據開始一直到畫到屏幕上。這整個過程就被叫作關鍵渲染路徑(critical rendering path)。

優化網頁性能就是優化這條關鍵渲染路徑。

通過良好優化的網站應該是漸進式加載的,而不會被整個阻止。

這就是一個網站快與慢的差別所在。 一個比較好的策略是讓瀏覽器優先加載哪些資源,加載資源的順序比較重要。(【譯註】好比:大部分都會將 js 放在 底部,css 放在頂部等。)

原文

相關文章
相關標籤/搜索