頁(yè)面生命周期內(nèi),有兩個(gè)非常重要的事件:
1.DOMContentLoaded
此時(shí)瀏覽器已經(jīng)完全加載了HTML文件,并且DOM樹已經(jīng)生成好了。但是其他外部資源,如樣式文件、圖片、字體等并沒(méi)有加載好
2.Load
此時(shí)瀏覽器已經(jīng)將所有的資源都加載完畢,可以正確讀取頁(yè)面中的資源。
兩個(gè)事件標(biāo)識(shí)了兩種不同的時(shí)刻:
1.DOMContentLoaded
DOM已經(jīng)完成加載,此時(shí)可以為這些DOM元素綁定事件,初始化接口等
2.Load
其他外部資源均已加載完成,可以正確讀出這些資源的信息,如圖片的寬高等
DOMContentLoaded
使用addEventListener監(jiān)聽(tīng)該事件:
document.addEventListener('DOMContentLoaded', ready)
舉一個(gè)??
<script>
function ready() {
alert('DOM is ready');
var img = document.getElementById('img')
// 由于圖片屬于外部資源,尚未下載完成,因此暫無(wú)法讀到數(shù)據(jù),所以結(jié)果是0x0
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
}
document.addEventListener("DOMContentLoaded", ready);
</script>

雖然DOMContentLoaded如我們期望的那樣工作,但仍在個(gè)別情況下有所不同
異步scripts
當(dāng)解析器(HTML Paser)讀取到<script>...</script>內(nèi)聯(lián)標(biāo)簽時(shí),會(huì)阻塞DOM構(gòu)建,它會(huì)立即執(zhí)行腳本,原因是可能這些腳本會(huì)影響DOM,因此需要等到這些腳本都執(zhí)行完成了,才會(huì)觸發(fā)DOMContentLoaded事件。
外部腳本(<script src="...">...</script>)也是如此,瀏覽器需要等到加載后再執(zhí)行完成才可以繼續(xù)執(zhí)行。
是否可以讓外部引用的腳本延后執(zhí)行,答案是可以的。async和defer屬性可以讓外部腳本延后執(zhí)行,而不阻塞瀏覽器解析文檔。async和defer僅在<script src="...">...</script>情形下起作用,對(duì)內(nèi)聯(lián)腳本是無(wú)效的。用戶可以在腳本完成加載前,就可以看到頁(yè)面了,提升了用戶體驗(yàn)。
async & defer
這兩個(gè)屬性都是告訴瀏覽器,標(biāo)記的腳本不需要等待它加載完成,瀏覽器可以繼續(xù)完成DOM構(gòu)建和渲染
兩者的區(qū)別如下:
| async | defer | |
|---|---|---|
| 執(zhí)行順序 | 誰(shuí)先完成下載誰(shuí)先執(zhí)行 | 執(zhí)行順序始終遵循加載的順序,即使先下載完成了 |
| DOMContentLoaded | 如果頁(yè)面加載時(shí)間較長(zhǎng),腳本可能會(huì)先執(zhí)行;絕大多數(shù)會(huì)在DOMContentLoaded之后執(zhí)行,但一定是在Load事件之前執(zhí)行 | 腳本會(huì)在DOMContentLoaded之前執(zhí)行,但不阻塞瀏覽器加載和解析文檔,即defer腳本與瀏覽器加載順序無(wú)關(guān)先后 |
因此async更符合一些應(yīng)用場(chǎng)景。

樣式阻塞
對(duì)于外部樣式文件而言,并不會(huì)影響到DOMCotentLoaded事件,它并不會(huì)等待外部樣式文件加載完成。
但是!?。?,如果一個(gè)腳本是緊跟著link樣式出現(xiàn),那么這個(gè)腳本就會(huì)等樣式加載完成了。如下:
<link type="text/css" rel="stylesheet" href="style.css">
<script>
// the script doesn't not execute until the stylesheet is loaded
alert(getComputedStyle(document.body).marginTop);
</script>
原因是瀏覽器猜測(cè)腳本可能會(huì)讀取一些樣式信息,如位置、顏色,顯然腳本就需要等到樣式的加載完成了。
這也是為什么我們最好把腳本文件放到HTML文檔的最后再加載,而不要把腳本放在head標(biāo)簽里,除非你清楚放在head標(biāo)簽里是必要的。
瀏覽器表單自動(dòng)填充
這個(gè)是順便引出的話題,你知道瀏覽器內(nèi)置的表單自動(dòng)填充在什么時(shí)候完成么?沒(méi)錯(cuò),就是DOMContentLoaded觸發(fā)的時(shí)候
window.onload
當(dāng)所有外部資源都完成加載后,瀏覽器觸發(fā)load事件,此時(shí)可以讀取外部資源信息了
<script>
window.onload = function() {
alert('Page loaded');
// image is loaded at this time
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
};
</script>

Chrome DevTool中的load

我們通??梢酝ㄟ^(guò)DOMContentLoaded的時(shí)間去衡量一個(gè)頁(yè)面加載的速度,因?yàn)榇藭r(shí)用戶已經(jīng)可以看到這個(gè)頁(yè)面,并可以有一些交互了。
兼容性
以上提到的使用document.addEventListener監(jiān)聽(tīng)DOMCotentLoaded事件,在IE9+都是有效的。需要兼容IE低版本
// 摘抄自jQuery源碼
if ( document.readyState === "complete" ||
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
window.setTimeout( jQuery.ready );
}
比較粗暴,反復(fù)去嗅探是否可以執(zhí)行ready函數(shù)。有興趣去讀讀jQuery源碼,很有意思