DOM解析和渲染與CSS、JS之間的關係

每一個前端在第一次寫一個完整功能的頁面,均可能會是這個樣子滴~css

<html>
  <head>
    <link rel="stylesheet" href="test.css">
  </head>
  <body>
    <div id="app"></div>
    
    <script src="test.js"></script>
  </body>
</html>
複製代碼

咱們都會被告知,css要放到head裏面,js要放到body尾部前面。固然都能說出一二,可是咱們仍是有必要了解一下究竟是爲啥。html

這裏面有涉及到 DOM,CSS,JS 互相之間的一些關係,接下來會分別介紹前端

DOM

DOM這裏有兩個概念,解析渲染瀏覽器

DOM解析:就是把你所寫的各類html標籤,生成一個DOM TREE,能夠認爲就是生成了一個最原始的頁面,一點樣式都沒有,毫無CSS修飾的那種;markdown

DOM渲染:瀏覽器會把自己默認的樣式+用戶本身寫得樣式整合到一塊兒,造成一個CSS TREE,而DOM渲染就是指DOM TREE 和 CSS TREE 結合到一塊兒,生成一個Render TREE,呈現出一個帶有樣式的頁面。網絡

線程

瀏覽器會有不一樣的線程,好比說app

  1. GUI 渲染線程異步

  2. JS 線程async

  3. 定時器觸發線程 (setTimeout)spa

  4. 瀏覽器事件線程 (onclick)

  5. http 異步線程

    ...

具體有關線程的內容,我會單獨寫一篇文章介紹,在這裏咱們只須要知道兩點:

  1. DOM的渲染對應的就是GUI渲染進程;JS的執行對應的就是JS線程;因此,DOM的渲染與JS代碼的運行,在同一瞬間只能有一個執行!
  2. 其餘幾個線程,用來輔助JS線程,不互斥,可是不是太影響JS線程和GUI線程兩個大佬,不是主角

阻塞

阻塞XXX是指讓XXX暫停了。好比JS的執行阻塞DOM解析,就是

DOM解析 --> JS執行(此時DOM解析暫停) --> JS執行完畢 --> DOM繼續解析

DOM與CSS

先看它倆之間的關係,也就是分析CSS的加載對DOM的解析和渲染的影響。

很明顯,DOM本身在那解析DOM TREE 和 css樣式有啥關係啊,因此css不影響DOM解析。

也很明顯,DOM渲染就是要生成樣式呢,確定和css有關係啊,因此css影響DOM渲染。

結論:

  1. css的加載不會阻塞DOM的解析
  2. css的加載阻塞DOM的渲染

DOM與JS

JS(加載和執行) 都會阻塞 DOM 的解析,由於JS中可能會對DOM進行操做,可能改變DOM的結構,因此JS的加載和執行是會阻塞DOM解析的。

JS(加載和執行) 都會阻塞 DOM 的渲染,同上面同樣,由於JS中可能對樣式進行操做。

注: html中每遇到< script >標籤,頁面就會從新渲染一次,由於要保證標籤中的JS代碼拿到的都是最新的樣式。

結論:

  1. JS的加載和執行會阻塞DOM的解析
  2. JS的加載和執行會阻塞DOM的渲染

CSS與JS

在線程那裏說了,CSS的加載會阻塞JS的執行,由於CSS的渲染GUI線程和JS運行線程互斥。 可是CSS不會阻塞JS的加載(瀏覽器能夠預先掃描並下載)

注1:

CSS、JS之間的加載是否阻塞,這裏不考慮,由於現代的瀏覽器都會預先偷看文檔,而且下載。

注2:

這裏的JS引入方式不包括async和defer

結論:

CSS的加載阻塞JS的運行,不阻塞JS的加載

三者

例1:

<header>
    <link href="test.css">
</header>
複製代碼

加載test.css是不會影響header的解析的,隻影響渲染

例2:

<header>
    <link href="test.css">
    <script src="test.js"></script>
</header>
複製代碼

加載test.css阻塞了test.js的運行,test.js的運行阻塞了header的解析和渲染,因此,看似test.css既阻塞了header的渲染,又阻塞了header的解析。

總結

  1. css的加載不會阻塞DOM的解析
  2. css的加載阻塞DOM的渲染
  3. JS的加載和執行會阻塞DOM的解析
  4. JS的加載和執行會阻塞DOM的渲染
  5. CSS的加載阻塞JS的運行,不阻塞JS的加載
  6. CSS的加載與JS的加載之間是否有影響?不考慮,瀏覽器自身會偷看並預先下載
  7. 遇到script標籤會觸發渲染,以便得到最新的樣式給JS代碼

注意: 有些文章可能會提到,css的加載會阻塞DOM的解析,那是由於,若是不存在js的話,不阻塞;若是存在js的話,css的加載阻塞js的運行,js的運行阻塞了DOM的解析,因此css間接的影響了DOM(因此一般會把css放在頭部,js放在body尾)

~~

補充

defer 和 asyc

由於上面介紹的都是同步的JS代碼,因此當 script 標籤中帶有 defer 和 async ,要區分

首先,這兩個都只能用於 script 外鏈文件的形式,不能把代碼寫到標籤裏

<script async>console.log("1")</script>
<script defer>console.log("2")</script>
複製代碼

上面兩種形式是無效的,仍是同步執行代碼

先放個熟悉的圖~

藍色線表明網絡讀取,紅色線表明執行時間,這倆都是針對腳本的;綠色線表明 HTML 解析。

defer

<script src="1.js" defer></script>
<script src="2.js" defer></script>
<script src="3.js" defer></script>
複製代碼

defer 屬性表示延遲執行引入 JavaScript,即 JavaScript 加載時 HTML 並未中止解析,這兩個過程是並行的。

整個 document 解析完畢且 defer-script 也加載完成以後(這兩件事情的順序無關),會執行全部由 defer-script 加載的 JavaScript 代碼,再觸發 DOMContentLoaded 事件。

defer 不會改變 script 中代碼的執行順序,示例代碼會按照 一、二、3 文件 的順序執行。

因此,defer 與相比普通 script,有兩點區別:

  • 載入 JavaScript 文件時不阻塞 HTML 的解析
  • 執行階段被放到 HTML 標籤解析完成以後。

async

async 屬性表示異步執行引入的 JavaScript,與 defer 的區別在於,若是已經加載好,就會開始執行,不管此刻是 HTML 解析階段仍是 DOMContentLoaded 觸發(HTML解析完成事件)以後。須要注意的是,這種方式加載的 JavaScript 依然會阻塞 load 事件。換句話說,async-script 可能在 DOMContentLoaded 觸發以前或以後執行,但必定在 load 觸發以前執行。

從上一段也能推出,多個 async-script 的執行順序是不肯定的,誰先加載完誰執行。值得注意的是,向 document 動態添加 script 標籤時,async 屬性默認是 true。

document.createElement

使用 document.createElement 建立的 script 默認是異步的,示例以下。

console.log(document.createElement("script").async); // true
複製代碼

因此,經過動態添加 script 標籤引入 JavaScript 文件默認是不會阻塞頁面的。若是想同步執行,須要將 async 屬性人爲設置爲 false。

若是使用 document.createElement 建立 link 標籤會怎樣呢?

const style = document.createElement("link");
style.rel = "stylesheet";
style.href = "index.css";
document.head.appendChild(style); // 阻塞?
複製代碼

其實這隻能經過試驗肯定,已知的是,Chrome 中已經不會阻塞渲染,Firefox、IE 在之前是阻塞的,如今會怎樣目前不太清楚。

轉載請註明出處,謝謝~~

相關文章
相關標籤/搜索