複習|js指令碼肯定會阻塞渲染?

語言: CN / TW / HK

theme: channing-cyan highlight: a11y-dark


js指令碼肯定會阻塞渲染嗎?

一、瀏覽器解析器遇到script指令碼的解析邏輯

1.1 情形一:頁面中引入的script指令碼會阻塞瀏覽器解析渲染文件。

瀏覽器解析文件時,預設是按照排列順序向下解析的,當遇到script標籤時,和其他標籤元素一樣(例如一個div),會先解析該元素(指令碼),解析完成後再繼續向下走完成剩餘文件的解析和渲染。也就是說預設情況下,script指令碼會阻塞文件的解析渲染。

注意,如果我們的script指令碼是放在頁面底部的內聯指令碼,那麼它對文件的解析渲染,在結果上影響不大。

但如果script指令碼是外部指令碼(通過網址引入的那種),那麼這個指令碼需要下載和解析執行,這期間會阻塞瀏覽器對文件的向下解析渲染,直至指令碼下載執行完成,才會繼續向下解析渲染。如果這個指令碼出錯了,可能還會導致整個頁面永遠無法正常渲染呈現。

注意,DOM樹的生成是受JavaScript程式碼執行影響的,JavaScript程式碼會“阻塞”頁面UI的渲染。

1.2 情形二:頁面中引入的script指令碼不會阻塞瀏覽器解析渲染文件。

情形一中提到的指令碼執行的同步和阻塞的情形,是指預設情況下的script指令碼載入方式。script標籤有兩個屬性,一個是defer(翻譯為延遲),二是asyn(翻譯為非同步,沒錯,和我們的常見的ajax和axios的非同步是一個意思),這兩個屬性都可以改變指令碼的載入執行方式(在瀏覽器支援的情況下,這兩的相容性,後面會補充)。

延遲:延遲是指當瀏覽器解析到script指令碼時,會繼續向下載入和解析文件,等到文件載入解析完成且可以操作文件時,才開始執行指令碼。

非同步:非同步是指當瀏覽器解析到script指令碼時,會立刻下載和執行指令碼,但同時瀏覽器解析器也會繼續向下解析渲染文件,不會造成阻塞的現象。這使得指令碼可以儘快的被載入執行,這很想我們前端呼叫post非同步介面,並不影響文件的正常解析渲染。

ps:defer和async屬性像是在告訴瀏覽器,連結進來的指令碼不會使用document.write(),也不會生成文件內容,因此瀏覽器在下載指令碼時可以繼續解析和渲染文件。

值得注意的是,使用defer屬性的指令碼,當有多個的時候,這些指令碼會按照他們在文件中排列的順序載入執行。而使用asyn屬性的指令碼,當有多個的時候,會順序開始觸發載入,但誰先完成載入就誰先執行,這就是說,他們的執行順序可能是無序不確定的。在有的時候知道這點很重要,因為這可能涉及一些有強制順序的邏輯處理。

二、script指令碼標籤的兩個特殊屬性:async與defer

2.1 注意事項

1.defer 屬性可能在不同瀏覽器不同版本上表現有所差異,需要注意其相容性和實現情況。

2.async屬性是 HTML5中的新屬性,僅適用於外部指令碼(即是在script標籤使用src屬性時)。

下面是一張來自 MDN 的相容性情況截圖。

image.png

image.png

2.2 外部指令碼執行情況彙總

  • 一個script標籤同時使用了async和defer,則執行async,忽略defer。

  • 一個script標籤只使用defer,且 defer="defer",則指令碼將在頁面完成解析時執行。

  • async和defer都不使用,遇到script指令碼即會馬上載入和執行指令碼,此時會阻塞頁面繼續向下解析渲染。

  • 只使用async且async="async",則指令碼相對於頁面的其餘部分非同步地執行。

三、寫在後面

所以,從瀏覽器解析器遇到script指令碼的解析邏輯來看,script指令碼可以阻塞瀏覽器繼續渲染,也可以不阻塞,分兩種情形:預設情況下,script 指令碼會阻塞瀏覽器繼續渲染,但可以通過 script 的 async 與 defer 屬性改變這種狀況。