首先來看看這三句話:
<script src="script.js"></script>
沒有 defer 或 async,浏覽器會立即加載並執行指定的腳本,“立即”指的是在渲染該 script 標簽之下的文檔元素之前,也就是說不等待後續載入的文檔元素,讀到就加載並執行。
<script async src="script.js"></script>
有 async,加載和渲染後續文檔元素的過程將和 script.js 的加載與執行並行進行(異步)。
<script defer src="myscript.js"></script>
有 defer,加載後續文檔元素的過程將和 script.js 的加載並行進行(異步),但是 script.js 的執行要在所有元素解析完成之後,DOMContentLoaded 事件觸發之前完成。
下面來進行詳解的介紹這二者的區別。
一般情況
按照慣例,所有script元素都應該放在頁面的head元素中。這種做法的目的就是把所有外部文件(CSS文件和JavaScript文件)的引用都放在相同的地方。可是,在文檔的head元素中包含所有JavaScript文件,意味著必須等到全部JavaScript代碼都被下載、解析和執行完成以後,才能開始呈現頁面的內容(浏覽器在遇到body標簽時才開始呈現內容)。
對於那些需要很多JavaScript代碼的頁面來說,這無疑會導致浏覽器在呈現頁面時出現明顯的延遲,而延遲期間的浏覽器窗口中將是一片空白。為了避免這個問題,現在Web應用程序一般都全部JavaScript引用放在body元素中頁面的內容後面。這樣一來,在解析包含的JavaScript代碼之前,頁面的內容將完全呈現在浏覽器中。而用戶也會因為浏覽器窗口顯示空白頁面的時間縮短而感到打開頁面的速度加快了。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> </head> <body> <script type="text/javascript" src="script.js"></script> </body> </html>
defer (延遲腳本)
延遲腳本:defer屬性只適用於外部腳本文件。
如果給script標簽定義了defer屬性,這個屬性的作用是表明腳本在執行時不會影響頁面的構造。也就是說,腳本會被延遲到整個頁面都解析完畢後再運行。因此,如果script元素中設置了defer屬性,相當於告訴浏覽器立即下載,但延遲執行。
示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>延遲加載</title> <script defer type="text/javascript" src="test.js"></script> </head> <body> </body> </html>
這個例子中,雖然我們把script元素放在了文檔的head元素中,但其中包含的腳本將延遲到浏覽器遇到</html>標簽後再執行。
HTML5規范要求腳本按照它們出現的先後順序執行,因此第一個延遲腳本會先於第二個延遲腳本執行,而這兩個腳本會先於DOMContentLoaded
事件(在DOM樹構建完成後觸發,不需要等到所有的資源都加載完畢)執行。
特別注意:在現實當中,延遲腳本並不一定會按照順序執行,也不一定會在DOMContentLoaded事件觸發前執行,因此最好只包含一個延遲腳本。
有 defer,加載後續文檔元素的過程將和 script.js 的加載並行進行(異步),但是 script.js 的執行要在所有元素解析完成之後,DOMContentLoaded
事件觸發之前完成。
最佳實踐
從實用角度來說,把所有腳本都放在 </body> 之前是最佳實踐,因為對於舊浏覽器來說這是唯一的優化選擇,此法可保證非腳本的其他一切元素能夠以最快的速度得到加載和解析。
**注意:**defer屬性在浏覽器之間表現並不一致。為了避免跨浏覽器的差異,可以使用 “lazy loading”的方法,即直到用到該腳本時才加載。
function lazyload() { var elem = document.createElement("script"); elem.type = "text/javascript"; elem.async = true; elem.src = "script.js"; document.body.appendChild(elem); } if (window.addEventListener) { window.addEventListener("load", lazyload, false); } else if (window.attachEvent) { window.attachEvent("onload", lazyload); } else { window.onload = lazyload; }
async(異步腳本)
異步腳本:async屬性也只適用於外部腳本文件,並告訴浏覽器立即下載文件。
但與defer不同的是:標記為async的腳本並不保證按照指定它們的先後順序執行。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>異步加載</title> <script async type="text/javascript" src="test1.js"></script> <script async type="text/javascript" src="test2.js"></script> </head> <body> </body> </html>
這個例子中,test2.js可能會在test1.js之前執行。因此,確保兩者之間互不一來非常重要。指定async屬性的目的是不讓頁面等待兩個腳本下載和執行,從而異步加載頁面其他內容。因此,建議異步腳本不要在加載期間修改DOM。
來看一張更加清晰的圖:
圖解:藍色線代表網絡讀取,紅色線代表執行時間,這兩個都是針對腳本的;綠色線代表 HTML 解析。
通過上圖和之前的分析,我們可以得出:
1、defer 和 async 在網絡讀取(腳本下載)這塊兒是一樣的,都是異步的(相較於 HTML 解析)
2、兩者的差別:在於腳本下載完之後何時執行,顯然 defer 是最接近我們對於應用腳本加載和執行的要求的。defer是立即下載但延遲執行,加載後續文檔元素的過程將和腳本的加載並行進行(異步),但是腳本的執行要在所有元素解析完成之後,DOMContentLoaded
事件觸發之前完成。async是立即下載並執行,加載和渲染後續文檔元素的過程將和js腳本的加載與執行並行進行(異步)。
3、關於 defer,我們還要記住的是它是按照加載順序執行腳本的
4、標記為async的腳本並不保證按照指定它們的先後順序執行。對它來說腳本的加載和執行是緊緊挨著的,所以不管你聲明的順序如何,只要它加載完了就會立刻執行。
5、async 對於應用腳本的用處不大,因為它完全不考慮依賴(哪怕是最低級的順序執行),不過它對於那些可以不依賴任何腳本或不被任何腳本依賴的腳本來說卻是非常合適的。
總結
以上就是關於js中defer和async區別的全部內容,文章介紹的很詳細,希望能對大家的學習或者工作帶來一定的幫助,如果有疑問大家可以留言交流。