本文譯自 -> 傳送門
作者:Ire Aderinokun |
譯者:fishc.com
JavaScript 被認(rèn)為是 “解析器阻塞資源”墨状。這意味著 HTML 文檔自身的解析將被 JavaScript 阻塞。即當(dāng)解析器到達(dá) <script> 標(biāo)簽時(shí)柑爸,無論是內(nèi)嵌還是引用外部代碼,解析器都會(huì)暫停 HTML 的解析盒音,轉(zhuǎn)而獲缺眵ⅰ(如果不是內(nèi)嵌)并執(zhí)行代碼。
這種行為邏輯可能會(huì)導(dǎo)致一些問題祥诽,比如當(dāng)我們?cè)陧撁嬷屑虞d多個(gè) JavaScript 文件時(shí)譬圣,無論實(shí)際上是否需要用到它們,瀏覽器都會(huì)將它們?nèi)枯d入原押,這樣一來胁镐,網(wǎng)頁的加載速度就受到了影響。
不過幸運(yùn)的是诸衔,script 元素?fù)碛袃蓚€(gè)屬性:async 和 defer盯漂,它們?cè)试S你控制代碼文件的獲取和執(zhí)行。
正常的執(zhí)行流程
在研究這兩個(gè)屬性之前笨农,我們先看看正常情況下代碼文件的執(zhí)行流程就缆。
在默認(rèn)情況下,如上所述谒亦,JavaScript 文件將中斷 HTML 文檔的解析竭宰,以便獲瓤战肌(如果不是內(nèi)嵌)并執(zhí)行代碼。
舉個(gè)例子切揭,下面代碼中 script 元素位于頁面中間的某個(gè)位置:
<html>
<head> ... </head>
<body>
...
<script src="script.js">
....
</body>
</html>
當(dāng)文檔解析器遍歷頁面遇到 <script> 標(biāo)簽時(shí)狞甚,會(huì)發(fā)生下面這種情況:
如上,HTML 解析將被代碼的獲取和執(zhí)行所中斷廓旬,從而延長(zhǎng)了第一次繪制網(wǎng)頁所需的時(shí)間哼审。
async屬性
async 屬性用于告訴瀏覽器該代碼可以被異步執(zhí)行。
HTML 解析器在遇到該 <script> 標(biāo)簽的時(shí)候不需要停下來孕豹,HTML 文檔的解析和代碼的獲取是同時(shí)進(jìn)行的涩盾,直到外部文件獲取完成,才會(huì)暫停下來執(zhí)行代碼励背。
<script async src="script.js">
該屬性僅適用于引用外部代碼文件的情況春霍。
當(dāng)文檔解析器遍歷頁面遇到 <script> 標(biāo)簽時(shí),會(huì)發(fā)生下面這種情況:
defer 屬性
defer 屬性用于告訴瀏覽器只有再 HTML 文檔全部解析完成之后再支持代碼文件叶眉。
<script defer src="script.js">
類似于異步加載代碼(async)址儒,代碼文件的獲取和 HTML 文檔的解析是可以并行的。然而竟闪,代碼并不會(huì)再獲取完成后立即執(zhí)行离福,它會(huì)等到所有的 HTML 文檔解析完畢后再執(zhí)行杖狼。
應(yīng)該選擇異步炼蛤,延遲還是正常執(zhí)行代碼?
那么問題來了蝶涩,什么時(shí)候我們應(yīng)該使用異步理朋,時(shí)候使用延遲,什么時(shí)候又讓它正常執(zhí)行绿聘?
這視具體情況而定嗽上,但決定前請(qǐng)先考慮下面幾個(gè)問題:
script 元素放置的位置?
只有當(dāng) script 元素沒有放在 HTML 的末尾時(shí)熄攘,異步和延遲執(zhí)行代碼才有意義兽愤。
從 <html> 到 </html>,HTML 文檔按順序被解析挪圾。如果在關(guān)閉 </body> 標(biāo)簽之前放置了一個(gè)引用外部的 JavaScript 文件浅萧,那么使用異步或延遲加載代碼就顯得意義不大,因?yàn)榇藭r(shí)解析器已經(jīng)完成了絕大部分 HTML 文檔的解析工作哲思。代碼是否獨(dú)立洼畅?
對(duì)于不依賴于其他文件或本身沒有任何依賴關(guān)系的代碼文件,async 屬性將會(huì)非常有用棚赔,因?yàn)槲覀儾⒉辉谝馑裁磿r(shí)候被執(zhí)行帝簇。這也是異步加載最常見的情況徘郭。代碼的執(zhí)行是否依賴于完全解析后的 DOM?
在很多情況下丧肴,代碼文件包含需要與 DOM 進(jìn)行交互的功能残揉。
或者,它可能依賴于頁面上包含的另一個(gè)文件芋浮。
在這些情況下冲甘,必須在代碼執(zhí)行之前先完全解析 DOM。
通常途样,這樣的代碼文件將被放置到頁面的底部江醇,以確保 DOM 的所有內(nèi)容已經(jīng)被解析完畢。
然而何暇,在出于任何原因需要將 script 元素放置在別處的情況下陶夜,可以使用 defer 屬性。是一個(gè)短小精悍的代碼裆站?
最后条辟,如果代碼尺寸相對(duì)較小,自身執(zhí)行時(shí)間也很短宏胯,則將其定義為內(nèi)嵌代碼可能更加合適羽嫡。
盡管內(nèi)嵌代碼會(huì)阻止 HTML 文檔的解析,但如果執(zhí)行代碼的時(shí)間很短肩袍,它應(yīng)該不是一個(gè)很大的干擾項(xiàng)杭棵。
另外,如果它依賴于其他文件氛赐,則可能需要短時(shí)間的阻塞(加載并)魂爪。
現(xiàn)代瀏覽器引擎的支持程度
各家瀏覽器開始支持 async 屬性的版本號(hào):
各家瀏覽器開始支持 defer 屬性的版本號(hào):
值得注意的是,這些屬性的行為在不同的 JavaScript 引擎中可能略有不同艰管。
例如滓侍,在 V8 中(Chromium),無論其屬性如何牲芋,都將嘗試在用于代碼執(zhí)行的單獨(dú)專用線程上解析所有代碼撩笆。在這種情況下,JavaScript 代碼文件的 “解析器阻塞” 影響應(yīng)該是控制在最小的缸浦。