在HTML頁面中加載腳本時,你需要小心,以避免影響頁面的加載性能。根據你在HTML頁面中添加腳本的位置和方式,將影響加載時間。
在HTML頁面中加載腳本時,你需要小心,以避免影響頁面的加載性能。
傳統上,腳本是以以下方式包含在頁面中:
<script src="script.js"></script>
當HTML解析器找到這行代碼時,將發出請求以獲取腳本並執行它。
一旦此過程完成,解析可以繼續,並分析頁面的其餘內容。
正如你可以想像的,此操作對頁面的加載時間產生了巨大影響。
如果腳本加載時間比預期的要長,例如如果網絡速度較慢,或者如果你使用的是移動設備且連接不穩定,訪問者可能會看到一個空白頁面,直到腳本加載並執行完成。
位置的重要性
當你初學HTML時,你被告知腳本標籤位於<head>
標籤中:
<html>
<head>
<title>標題</title>
<script src="script.js"></script>
</head>
<body>
...
</body>
</html>
正如之前所說,當解析器找到這行代碼時,它將獲取腳本並執行它。 然後,在完成這個任務之後,它繼續解析body。
這樣做是不好的,因為會引入很多延遲。解決這個問題的一個常見解決方案是將script
標籤放在頁面底部,即在結束的</body>
標籤之前。
這樣做可以在頁面已經解析和加載完成之後加載和執行腳本,這對於不支持HTML的兩個相對新的功能(async
和defer
)的舊瀏覽器來說是一個巨大的改進。
如果你需要支持不支持async
和defer
的舊瀏覽器,這是你可以做的最好的事情。
async和defer
async
和defer
都是布爾屬性。它們的使用方式類似:
<script async src="script.js"></script>
<script defer src="script.js"></script>
如果你同時指定了這兩個屬性,對於現代瀏覽器來說,async
的優先級更高,而對於支持defer
但不支持async
的舊瀏覽器,則會回退到使用defer
。
有關支持情況的詳細信息,可以參考 caniuse.com 中的 async https://caniuse.com/#feat=script-async 和 defer https://caniuse.com/#feat=script-defer
只有在將腳本放在頁面的head
部分時,這些屬性才有意義,如果將腳本放在上面看到的body
底部,則這些屬性將無效。
性能比較
沒有defer或async,在head中
以下是一個在head部分中未使用defer或async的腳本加載方式:
解析過程在獲取腳本並執行之前暫停,腳本執行完成後解析繼續。
沒有defer或async,在body中
以下是一個在body標籤結束之前並未使用defer或async的腳本加載方式:
解析過程不會暫停,並且腳本在解析完成之後被獲取並執行。解析在腳本甚至下載之前完成,因此頁面在之前的示例中更早地顯示給用戶。
使用async,在head中
以下是一個在head標籤中使用async
的腳本加載方式:
腳本異步獲取,當腳本準備好時,HTML解析將暫停以執行腳本,然後繼續解析。
使用defer,在head中
以下是一個在head標籤中使用defer
的腳本加載方式:
腳本異步獲取,並且僅在HTML解析完成後才執行。
解析完成的方式與將腳本放在body標籤結束時相同,但是腳本執行的時間總體上更早,因為腳本已經在HTML解析期間並行下載。
因此,就速度而言,這是最好的解決方案🏆
阻塞解析
async
會阻塞頁面的解析,而defer
則不會。
阻塞渲染
無論是async
還是defer
都不能保證阻塞渲染。這取決於你的腳本和你的腳本是否阻塞渲染(例如,確保你的腳本在onLoad
事件之後運行)。
domInteractive
標記為defer
的腳本在domInteractive
事件之後執行,該事件在頁面加載、解析並構建DOM之後發生。
此時,CSS和圖像仍需解析並加載。
一旦完成,瀏覽器將觸發domComplete
事件,然後是onLoad
事件。
domInteractive
很重要,因為其時間被認為是感知加載速度的一個衡量標準。有關詳細信息,請參見MDN文檔:https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming/domInteractive。
保持順序
再來考慮defer
的情況:標記為async
的腳本按照隨機順序執行,一旦它們可用。標記為defer
的腳本按照在標記中定義的順序(在HTML中)執行。
告訴我最好的方法
在使用腳本時,為了加快頁面加載速度,最好將它們放在head中,並在script
標籤中添加defer
屬性:
<script defer src="script.js"></script>
這是觸發更快的domInteractive
事件的情景。
考慮到defer
的優點,它在各種情境下似乎是一個更好的選擇,除非你可以接受延遲頁面的首次渲染,確保在解析頁面時,你希望執行的JavaScript已經執行。