引言
JS的“加載”不能理解為下載,它是分為兩個部分:下載,執行。默認的JS加載是同步的,因為瀏覽器需要一個穩定的DOM結構,而執行JS時可能會對DOM造成改變,所以在執行JS時一定會阻塞HTML的渲染。我們可以使用一些方法使JS的下載不會阻塞HTML渲染,但不能使JS的執行阻塞渲染。
以下從幾個方面解釋JS的加載:
1、同步加載
2、異步加載
2.1、Script DOM Element
2.2、onload 時的異步加載
1、同步加載
同步模式,又稱阻塞模式,會阻止瀏覽器的后續處理,停止了后續的解析,因此停止了后續的文件加載(如圖像)、渲染、代碼執行。
使用方式如下:
<script src="http://domin1.com/script1.js"></script>
<script type="text/javascript">
console.log('hello world');
</script>
這段代碼中,如果請求的 script1.js 加載時間過長(domin1 被牆等原因),瀏覽器就會停止后續的文件下載,渲染,代碼執行,第二個<script>中的"hello world"也無法顯示。以前的處理方法是把<script>放置於頁面末尾的</body>之前,可以使頁面先顯示出來,但這也只是另頁面渲染不被阻塞而已,對於本例中第二個<script>的JS執行所造成的阻塞是無能為力的。
2、異步加載
異步加載又叫非阻塞,瀏覽器在下載 JS 同時,還會繼續進行后續頁面的處理,上述提到的第二個<script>的JS執行便不會被阻塞。
2.1、Script DOM Element 法
此方法不要求JS同源,使用方式如下:
(function (){ var async_script = document.createElement('script'); async_script.type = "text/javascript"; async_script.src = "http://domin.com/script.js"; var x = document.getElementsByTagName("script")[0]; x.ParentNode.insertBefore( async_script, x); })();
以上代碼使用 JS 創建了一個<script>插入到 document 中,便實現了異步加載JS。
將JS代碼包裹在匿名函數中並立即執行的方式是為了保護變量名泄露到外部可見,比較常見。
但問題還是存在:這段代碼在下載和執行完之前會阻止 onload 事件的觸發,於是有了onload 時的異步加載。
2.2、onload 時的異步加載
(function() { function async_load(){ var as = document.createElement("script"); as.type = "text/javascript"; as.src = "http://domain1.com/script.js"; var x = document.getElementsByTagName("script")[0]; x.parentNode.insertBefore(s, x); } if (window.attachEvent) window.attachEvent('onload', async_load); else window.addEventListener('load', async_load, false); })();
這段代碼與之前區別在於它不是馬上異步加載 JS ,而是在 onload 開始時才異步加載,便不會阻塞 onload 事件觸發。
2.3、async 和 defer 屬性
1. defer 屬性
//async屬性: <script src="script1.js" async="async"></script> //defer屬性: <script src="script2.js" defer="defer"></script>
async屬性使加載和渲染后續文檔元素的過程將和當前js的加載與執行並行進行(異步);defer屬性使下載后續文檔元素的過程將和 script.js 的下載並行進行(異步),但是 script.js 的執行要在所有元素(DOM)解析完成之后,DOMContentLoaded 事件觸發之前完成。但async屬性是HTML5新增的,使用時需注意兼容性問題。
此處盜了個圖:
該圖中,綠線為HTML解析,藍線為腳本下載,紅線為腳本執行。我們可以看出默認情況下也就是同步模式下,腳本的下載和執行會阻塞HTML解析;異步模式中的async和defer兩個屬性在下載時均為異步不影響HTML解析,在執行均阻塞HTML解析,但defer勝在它可以控制腳本執行的時間,保證頁面先加載出之后再執行腳本。
3、總述
除defer外,瀏覽器在下載完 JS 的內容后就會立即對其解析和執行,不管是同步加載還是異步加載。
文章中提到的異步加載,只能做到下載時的異步,但執行的時候依然會阻塞瀏覽器任何操作。
利用特殊的技巧可以做到 下載 與 執行的分離,下次再寫了,天色太晚困的慌~。