這篇文章是第二篇,第一篇在這裏。javascript
第一篇大體講到了瀏覽器從獲取原始數據開始,直到把內容畫到屏幕上,可是尚未完。css
以後,還有第三篇(整理)在這裏html
當你聽到「渲染阻塞」的時候,你會想到啥?我猜是,‘某些行爲阻止了瀏覽器把內容畫到屏幕上’。java
的確是你猜的那樣。瀏覽器
因此這裏有了咱們的第一個優化點,把最重要的HTML內容和CSS樣式提供給客戶端,越快越好。服務器
DOM和CSSOM都必須在屏幕繪製以前構造完成,因此HTML
和CSS
都是渲染阻塞資源。網絡
因此關鍵點就是你應該讓客戶端竟可能快地獲取你的html
和css
,這樣就可以優化首屏渲染的時間。app
如今,基本只要是個網站都有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以後的腳本哈。)
由於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。
你有注意到這個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
爲何?
其實很簡單。當HTML解析器正在建立DOM樹的時候,發現有一個script
標籤,而此時,body
標籤以及它的內容尚未解析出來。DOM構建被暫停直到這個script執行完。
這裏又帶出了另外一個重要的點。
你腳本的位置很重要。
若是你以文件形式加載一個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建立好。
因此當遇到script
的時候,DOM的構建會中止,可是CSSOM並不會(【譯註】不但不會,還會暫停js的執行)。
【譯註】這裏我想要補充一下,經過上面所說的,咱們能夠推出一個觀點:
JS 文件不僅是阻塞 DOM 的構建,它會致使 CSSOM 也阻塞 DOM 的構建。
本來 DOM 和 CSSOM 的構建是互不影響,井水不犯河水,可是一旦引入了 JavaScript,CSSOM 也開始阻塞 DOM 的構建。由於不完整的 CSSOM 是沒法使用的,若是 JavaScript 想訪問 CSSOM 並更改它,那麼在執行 JavaScript 時,必需要能拿到完整的 CSSOM。因此就致使了一個現象,若是瀏覽器還沒有完成 CSSOM 的下載和構建,而咱們卻想在此時運行腳本,那麼瀏覽器將延遲腳本執行和 DOM 構建,直到完成 CSSOM 的建立。也就是說,在這種狀況下,瀏覽器會先去構建 CSSOM,而後再執行 JavaScript,最後在繼續構建 DOM。
默認狀況下,每一個script都會組織DOM樹的建立。不夠有一個方法可以改變這一個行爲。
若是你在script
標籤里加一個async
屬性,那麼DOM樹的建立就不會暫停,這個腳本就會在下載完後去執行。例如:
<script src="https://some-link-to-app.js" async></script>
咱們以前所說的從獲取html,css和js原始數據開始一直到畫到屏幕上。這整個過程就被叫作關鍵渲染路徑(critical rendering path)。
優化網頁性能就是優化這條關鍵渲染路徑。
通過良好優化的網站應該是漸進式加載的,而不會被整個阻止。
這就是一個網站快與慢的差別所在。 一個比較好的策略是讓瀏覽器優先加載哪些資源,加載資源的順序比較重要。(【譯註】好比:大部分都會將 js 放在 底部,css 放在頂部等。)