1. 程式人生 > 程式設計 >js與css的阻塞問題詳析

js與css的阻塞問題詳析

目錄
  • DOMContentLoaded和load
  • 阻塞了什麼
  • 阻塞了什麼
  • 優化
  • 總結

DOMContentLoaded和load

我們先了解兩個事件,有助於後面的分析。

load事件:load 應該僅用於檢測一個完全載入的頁面 當一個資源及其依賴資源已完成載入時,將觸發load事件。也就是說,頁面的html、css、js、圖片等資源都已經載入完之後才會觸發 load 事件。

DOMContentLoaded事件:當初始的 HTML 文件被完全載入和解析http://www.cppcns.com完成之後,DOMConthttp://www.cppcns.comentLoaded 事件被觸發,而無需等待樣式表、影象和子框架的完成載入。也就是說,DOM 樹已經構建完畢就會觸發 DOMContentLoaded 事件。

js 阻塞了什麼

因為js在執行的過程中可能會操作DOM,發生迴流和重繪,所以GUI渲染執行緒與JS引擎執行緒是互斥的。

在解析HTML過程中,如果遇到 script 標籤,渲染執行緒會暫停渲染過程,將控制權交給 JS 引擎。內聯的js程式碼會直接執行,如果是js外部檔案,則要下載該js檔案,下載完成之後再執行。等 JS 引擎執行完畢,瀏覽器又會把控制權還給渲染執行緒,繼續 DOM 的解析。

因此,js會阻塞DOM樹的構建。

那麼,是否會阻塞頁面的顯示呢?我們用下面的程式碼來測試一下。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>hello world</div>
  <script>
    debugger
  </script>
  <div>hello world2</div>
</body>
</html>

js與css的阻塞問題詳析

可以看到,這個頁面的DOMContentLoaded發生在2.23s,可見js阻塞了DOM樹的構建。但是,頁面上卻幾乎在一瞬間顯示了hello world,說明js不會阻塞位於它之前的dom元素的渲染。

現代瀏覽器為了更好的使用者體驗,渲染引擎將嘗試儘快在螢幕上顯示的內容。它不會等到所有DOM解析完成後才佈局渲染樹。而是當js阻塞發生時,會將已經構建好的DOM元素渲染到螢幕上,減少白屏的時間。

這也是為什麼我們會將script標籤放到body標籤的底部,因為這樣就不會影響前面的頁面的渲染。

css 阻塞了什麼

當我們解析 HTML 時遇到 link 標籤或者 style 標籤時,就會計算樣式,構建CSSOM。

css不會阻塞dom樹的構建,但是會阻塞頁面的顯示。我們依然用一個例子來測試:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UAhttp://www.cppcns.com-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" type="text/css" href="https://h5.sinaimg.cn/m/weibo-pro/css/chunk-vendors.d6cac585.css">
</head>
<body>
  <div class="woo-spinner-filled">hello world</div>
  <div>hello world2</div>
</body>
</html>

js與css的阻塞問題詳析

使用一個外部css檔案,開啟Slow 3G模擬比較慢的網速,可以看到,DOMContentLoaded事件觸發只用了30ms,頁面此時依然是空白,而幾乎是loaded事件2.92s發生時,頁面才出現內容。

原因是,瀏覽器在構建 CSSOM 的過程中,不會渲染任何已處理的內容。即便 DOM 已經解析完畢了,只要 CSSOM 不沒構建好,頁面也不會顯示內容。

只有當我們遇到 link 標籤或者 style 標籤時,才會構建CSSOM,所以如果 link 標籤之前有dom元素,當載入css發生阻

<body>
  <div class="woo-spinner-filled">hello world</div>
  <link rel="stylesheet" type="text/css" href="https://h5.sinaimg.cn/m/weibo-pro/css/chunk-vendors.d6cac585.css">
  <div>hello world2</div>
</body>

這樣做會導致一個問題,就是頁面閃爍,在css被載入之前,瀏覽器按照預設樣式渲染 <div class="woo-spinner-filled">hello world</div>,當css載入完成,會為該div計算新的樣式,重新渲染,出現閃爍的效果。

為了避免頁面閃爍,通常 link 標籤都放在head中。

css會不會阻塞後面js執行?答案是會!

JS 的作用在於修改,它幫助我們修改的方方面面:內容、樣式以及它如何響應使用者互動。這“方方面面”的修改,本質上都是對 DOM 和 CSSDOM 進行修改。當在JS中訪問了CSSDOM中某個元素的樣式,那麼這時候就需要等待這個樣式被下載完成才能繼續往下執行JS。

執行下面這個例子,就會發現等css載入完成後,才會在控制檯列印“this is a test”。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" type="text/css" href="https://h5.sinaimg.cn/m/weibo-pro/css/chunk-vendors.d6cac585.css">
</head>
<body>
  <div class="woo-spinner-filled">hello world</div>
  <div>hello world2</div>
  <script>
    console.log('this is a test')
  </script>
</body>
</html>

優化

使用內聯 和 CSS,這樣獲取到 HTML 檔案之後就可以直接開始渲染流程了。

並不是所有的場合都適合內聯,那麼還可以儘量減少檔案大小,比如通過 webpack 等構建工具刪除無用程式碼、壓縮 css、Script 檔案的體積;並且啟用 CDN 加快檔案的下載速度。

對於大的 CSS 檔案,可以通過媒體查詢屬性,將其拆分為多個不同用途的 CSS 檔案,這樣只有在特定的場景下才會載入特定的 CSS 檔案。

如果 JavaScript 檔案中沒有操作 DOM 相關程式碼,就可以將該 JavaScript 指令碼設定為非同步載入,通過 async 或 defer 來標記程式碼。

<script src="index.js"></script>
//瀏覽器必須等待 index.js 載入和執行完畢才能去做其它事情。

<script async src="index.js"></script>
//index.js 的載入是非同步的,載入時不會阻塞瀏覽器做任何其它的事情。
//當它載入結束,JS 指令碼會立即執行。

<script defer src="index.js"></script>
//JS 的載入是非同步的,執行是被推遲的。
//使用了 defer 標記的指令碼檔案,會等整個文件解析完成,在 DOMContentLoaded 事件觸發之前執行

總結

到此這篇關於js與css的阻塞問題的文章就介紹到這了,更多相關js與css阻塞內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!