原來 CSS 與 JS 是這樣阻塞 DOM 解析和渲染的

hello~各位親愛的看官老爺們你們好。估計你們都聽過,儘可能將CSS放頭部,JS放底部,這樣能夠提升頁面的性能。然而,爲何呢?你們有考慮過麼?很長一段時間,我都是知其然而不知其因此然,強行背下來應付考覈固然能夠,但實際應用中必然一塌糊塗。所以洗(wang)心(yang)革(bu)面(lao),小結一下最近玩出來的成果。css

友情提示,本文也是小白向爲主,若是直接想看結論能夠拉到最下面看的~html


因爲關係到文件的讀取,那是確定須要服務器的,我會把所有的文件放在github上,給我點個 star 我會開心!掘金上再給我點個 我就更開心了~node

node端惟一須要解釋一下的是這個函數:git

function sleep(time) {
  return new Promise(function(res) {
    setTimeout(() => {
      res()
    }, time);
  })
}
複製代碼

嗯!其實就延時啦。若是CSS或者JS文件名有sleep3000之類的前綴時,意思就是延遲3000毫秒纔會返回這文件。github

下文使用的HTML文件是長這樣的:數組

<!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Title</title>
    	<style>
    		div {
    			width: 100px;
    			height: 100px;
    			background: lightgreen;
    		}
    	</style>
    </head>
    <body>
    	<div></div>
    </body>
    </html>
複製代碼

我會在其中插入不一樣的JSCSS瀏覽器

而使用的common.css,不論有沒有前綴,內容都是這樣的:bash

div {
  background: lightblue;
}
複製代碼

好了,話很少數,開始正文!服務器

CSS

關於CSS,你們確定都知道的是<link>標籤放在頭部性能會高一點,少一點人知道若是<script><link>同時在頭部的話,<script>在上可能會更好。這是爲何呢?下面咱們一塊兒來看一下CSSDOM的影響是什麼。dom

CSS 不會阻塞 DOM 的解析

注意哦!這裏說的是DOM 解析,證實的例子以下,首先在頭部插入<script defer src="/js/logDiv.js"></script>JS文件的內容是:

const div = document.querySelector('div');
console.log(div);
複製代碼

defer屬性相信你們也很熟悉了,MDN對此的描述是用來通知瀏覽器該腳本將在文檔完成解析後,觸發 DOMContentLoaded 事件前執行。設置這個屬性,能保證DOM解析後立刻打印出div

以後將<link rel="stylesheet" href="/css/sleep3000-common.css">插入HTML文件的任一位置,打開瀏覽器,能夠看到是首先打印出div這個DOM節點,過3s左右以後才渲染出一個淺藍色的div。這就證實了CSS 是不會阻塞 DOM 的解析的,儘管CSS下載須要3s,但這個過程當中,瀏覽器不會傻等着CSS下載完,而是會解析DOM的。

這裏簡單說一下,瀏覽器是解析DOM生成DOM Tree,結合CSS生成的CSS Tree,最終組成render tree,再渲染頁面。因而可知,在此過程當中CSS徹底沒法影響DOM Tree,於是無需阻塞DOM解析。然而,DOM TreeCSS Tree會組合成render tree,那CSS會不會頁面阻塞渲染呢?

CSS 阻塞頁面渲染

其實這一點,剛纔的例子已經說明了,若是CSS 不會阻塞頁面阻塞渲染,那麼CSS文件下載以前,瀏覽器就會渲染出一個淺綠色的div,以後再變成淺藍色。瀏覽器的這個策略其實很明智的,想象一下,若是沒有這個策略,頁面首先會呈現出一個原始的模樣,待CSS下載完以後又忽然變了一個模樣。用戶體驗可謂極差,並且渲染是有成本的。

所以,基於性能與用戶體驗的考慮,瀏覽器會盡可能減小渲染的次數,CSS瓜熟蒂落地阻塞頁面渲染。

然而,事情總有奇怪的,請看這例子,HTML頭部結構以下:

<header>
    <link rel="stylesheet" href="/css/sleep3000-common.css">
    <script src="/js/logDiv.js"></script>
</header>
複製代碼

但思考一下這會產生什麼結果呢?

答案是瀏覽器會轉圈圈三秒,但此過程當中不會打印任何東西,以後呈現出一個淺藍色的div,再打印出null。結果好像是CSS不單阻塞了頁面渲染,還阻塞了DOM 的解析啊!稍等,在你打算掀桌子瘋狂吐槽我以前,請先思考一下是什麼阻塞了DOM 的解析,剛纔已經證實了CSS是不會阻塞的,那麼阻塞了頁面解析實際上是JS!但明明JS的代碼如此簡單,確定不會阻塞這麼久,那就是JS在等待CSS的下載,這是爲何呢?

仔細思考一下,其實這樣作是有道理的,若是腳本的內容是獲取元素的樣式,寬高等CSS控制的屬性,瀏覽器是須要計算的,也就是依賴於CSS。瀏覽器也沒法感知腳本內容究竟是什麼,爲避免樣式獲取,於是只好等前面全部的樣式下載完後,再執行JS。於是形成了以前例子的狀況。

因此,看官大人明白爲什麼<script><link>同時在頭部的話,<script>在上可能會更好了麼?之因此是可能,是由於若是<link>的內容下載更快的話,是沒影響的,但反過來的話,JS就要等待了,然而這些等待的時間是徹底沒必要要的。

JS

JS,也就是<script>標籤,估計你們都很熟悉了,不就是阻塞DOM解析和渲染麼。然而,其中其實仍是有一點細節能夠考究一下的,咱們一塊兒來好好看看。

JS 阻塞 DOM 解析

首先咱們須要一個新的JS文件名爲blok.js,內容以下:

const arr = [];
for (let i = 0; i < 10000000; i++) {
  arr.push(i);
  arr.splice(i % 3, i % 7, i % 5);
}
const div = document.querySelector('div');
console.log(div);
複製代碼

其實那個數組操做時沒意義的,只是爲了讓這個JS文件多花執行時間而已。以後把這個文件插入頭部,瀏覽器跑一下。

結果估計你們也能想象獲得,瀏覽器轉圈圈一會,這過程當中不會有任何東西出現。以後打印出null,再出現一個淺綠色的div。現象就足以說明JS 阻塞 DOM 解析了。其實緣由也很好理解,瀏覽器並不知道腳本的內容是什麼,若是先行解析下面的DOM,萬一腳本內全刪了後面的DOM,瀏覽器就白乾活了。更別談喪心病狂的document.write。瀏覽器沒法預估裏面的內容,那就乾脆所有停住,等腳本執行完再幹活就行了。

對此的優化其實也很顯而易見,具體分爲兩類。若是JS文件體積太大,同時你肯定不必阻塞DOM解析的話,不妨按須要加上defer或者async屬性,此時腳本下載的過程當中是不會阻塞DOM解析的。

而若是是文件執行時間太長,不妨分拆一下代碼,不用當即執行的代碼,可使用一下之前的黑科技:setTimeout()。固然,現代的瀏覽器很聰明,它會「偷看」以後的DOM內容,碰到如<link><script><img>等標籤時,它會幫助咱們先行下載裏面的資源,不會傻等到解析到那裏時才下載。

瀏覽器遇到 <script> 標籤時,會觸發頁面渲染

這個細節可能很多看官大人並不清楚,其實這纔是解釋上面爲什麼JS執行會等待CSS下載的緣由。先上例子,HTMLbody的結構以下:

<body>
	<div></div>
	<script src="/js/sleep3000-logDiv.js"></script>
	<style>
		div {
			background: lightgrey;
		}
	</style>
	<script src="/js/sleep5000-logDiv.js"></script>
	<link rel="stylesheet" href="/css/common.css">
</body>
複製代碼

這個例子也是很極端的例子,但不妨礙它透露給咱們不少重要的信息。想象一下,頁面會怎樣呢?

答案是先淺綠色,再淺灰色,最後淺藍色。因而可知,每次碰到<script>標籤時,瀏覽器都會渲染一次頁面。這是基於一樣的理由,瀏覽器不知道腳本的內容,於是碰到腳本時,只好先渲染頁面,確保腳本能獲取到最新的DOM元素信息,儘管腳本可能不須要這些信息。

小結

綜上所述,咱們得出這樣的結論:

  • CSS 不會阻塞 DOM 的解析,但會阻塞 DOM 渲染。
  • JS 阻塞 DOM 解析,但瀏覽器會"偷看"DOM,預先下載相關資源。
  • 瀏覽器遇到 <script>且沒有deferasync屬性的 標籤時,會觸發頁面渲染,於是若是前面CSS資源還沒有加載完畢時,瀏覽器會等待它加載完畢在執行腳本。

因此,你如今明白爲什麼<script>最好放底部,<link>最好放頭部,若是頭部同時有<script><link>的狀況下,最好將<script>放在<link>上面了嗎?

感謝各位看官大人看到這裏,但願本文對你有所幫助,有不一樣或更好意見的大佬,還望不吝賜教!謝謝~

相關文章
相關標籤/搜索