[JS] Async, defer attributes
- 👍 Scripts: async, defer @ JavaScript.info
- 圖片來源:async vs defer attributes @ Growing with the web
TL;DR
defer
會讓 scripts 的檔案先開始被下載,但在 HTML 文件準備好後才開始執行,同時會確保各個 script 檔案執行的順序async
會讓 scripts 的檔案先開始被下載,但它不會確保各個 script 檔案被執行的順序,先下載好的就先執行
在 HTML 中載入 JS 三種常用的方式
只使用 script
<!-- https://javascript.info/script-async-defer -->
<p>...content before script...</p>
<script src="demo.js"></script>
<!-- 當 JS 還沒下載與解析完前,HTML 的繪製會卡在這,因此需等到 script 載入完後,才會看到下面的內容 -->
<p>...content after script...</p>
執行到這段程式碼時,整個網頁的繪製會停下,等 demo.js
下載完並執行完後,網頁繪製才繼續。
defer
<script src="demo1.js" defer></script>
<script src="demo2.js" defer></script>
網頁繪製不會停下, demo.js
在背景下載,不會阻塞頁面的繪製,並且會等到 DOM 準備好後(但 DOMContentLoaded
事件發生前)才加以執行。因此相當實用,且目前瀏覽器多已經支援。
和傳統只使用 <script>
的方式一樣,使用 defer
能夠保證 scripts 的檔案會按照 document 中的順序執行,以上面的程式碼為例,demo1.js
和 demo2.js
會同時才背景開始被下載,但即使 demo2.js
比較早下載完成,瀏覽器仍會確保 demo1.js
執行完後才執行 demo2.js
。
defer
適合用在需要等待 DOM 完成後才能被執行的 JS,或者多個 JS 檔彼此之間有相依性的情況。
async
<script src="demo1.js" async></script>
<script src="demo2.js" async></script>
和 defer
類似的是 demo1.js
和 demo2.js
都會在背景下載,但不同的地方在於,async
會在該 JS 檔下載完成後,就會馬上被執行,它不會等待 DOM ready 後才執行,也不保證各 script 間執行的順序,也就是說,如果 demo2.js
比 demo1.js
還快被下載完成的話,它就會先被執行。
因此,async
適合用在每個 script
的 JS 檔彼此之 間沒有相依性,不需要誰先執行才能換另一個執行的這種情況(例如,Google Analytics)。
async
下載完就馬上執行,不需要等待 DOM ready。 適合用在各 script 間的 JS 檔沒有相依,且不需要等到 DOM ready 才能執行的情況下使用,例如 Ads、GA 等等。
defer 和 body script 的不同
傳統上,為了解決單純使用 script 會卡住瀏覽器繪製,以及 HTML 可能還沒完全繪製完,會取不到 DOM 的問題(例如,jQuery 取不到元素),因此會把 <script>
放到 <body>
的最後才執行,以避免上述提到的問題,像是這樣:
<body>
<!-- other html -->
<script src="demo.js"></script>
</body>
這麼做雖然看起來看 defer
的效果很像,但最大的差別是該 JS 檔開始被下載的時間點。如果是 defer
的話這隻 JS 檔很早就開始被「下載」,直到 DOM 完全建置好後(但會在 DOMContentLoaded
事件前)才被「執行」;但把 <script>
放到 <body>
的最後,則是要等這段語法被解析到時才會開始「下載」JS 檔,接著立即執行。