一、DOMContentLoaded 與 load事件
關於load和DOMContentLoaded事件,mdn對於它們是這樣描述的:
DOMContentLoaded
mdn文檔地址:https://developer.mozilla.org/zh-CN/docs/Web/Events/DOMContentLoaded
The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.
意思就是:當初始的 HTML 文檔被完全加載和解析完成之后,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。
load
mdn文檔地址:https://developer.mozilla.org/zh-CN/docs/Web/Events/load
The load event is fired when a resource and its dependent resources have finished loading.
意思就是:當一個資源及其依賴資源已完成加載時,將觸發load事件。
區別小結
簡而言之,二者觸發時間的區別在於:DOMContentLoaded在HTML文檔被解析完成之后觸發,而load是在HTML所有相關資源被加載完成后觸發。
為了感受這兩個事件,可以使用Chrome打開一個任意一個網頁。打開控制台的Network面板。以下是FT中文網首頁的Network面板Waterfall截圖:
可以看到圖上有兩條線:一條藍線,代表DOMContentLoaded事件,觸發時間為1.50s;一條紅線,代表load事件,觸發時間為5.54s。
如果想要更直觀地感受二者的區別,還可以點擊這個頁面:https://testdrive-archive.azurewebsites.net/HTML5/DOMContentLoaded/Default.html
二、HTML解析過程與DOMContentLoaded觸發時機
我們已經知道DOMContentLoaded的觸發時間為:當 HTML文檔被加載和解析完成。那么我們還需要理解HTML的解析過程。
此處我們先只考慮同步js的情況。
1.在既沒有CSS也沒有JS的情況下,HTML文檔的解析過程為:
DOMContentLoaded事件的觸發時機為:HTML解析為DOM之后。
2.有CSS無JS的情況下,HTML文檔解析過程為:
這里與1.不同的地方在於,渲染樹的生成是基於DOM和CSSOM的。但是觸發DOMContentLoaded的時間依然是在HTML解析為DOM后,無論此時CSS解析為CSSOM的過程是否完成。
3.當有JS時,HTML文檔解析過程為:
有一個問題:關於首屏時間?
“計算這個網頁從空白到出現內容所花費的時間”。那怎么計算這段時間?這段時間其實就是HTML 文檔加載和解析的時間。也就是DOMContentLoaded 事件觸發之前所經歷的時間。
所以,對於首屏時間而言,js放在HTML文檔的開頭和結尾處效果是一樣的而js放在結尾的目的並不是為了減少首屏時間,而是由於js經常需要操縱DOM,放在后面才更能保證找到DOM節點。待進一步探究
三、異步腳本、延遲腳本與DOMContentLoaded的關系
sync
為了與異步腳本和延遲腳本進行一個更清晰的對比,在這里先將同步腳本的情況分析一下。
如上圖所示, HTML 文檔被解析時如果遇見(同步)腳本,則停止解析,先去加載腳本,然后執行,執行結束后繼續解析 HTML 文檔。HTML文檔解析完畢后觸發DOMContentLoaded。
async
對此,《JavaScript高級程序設計》一書的解釋是:帶async的腳本一定會在load事件之前執行,可能會在DOMContentLoaded之前或之后執行。
為什么async腳本可能會在DOMContentLoaded之前或之后執行呢?或者說,為什么DOMContentLoaded事件的觸發既可能在async腳本執行前、又可能在async腳本執行后呢? 這是因為,async 標簽的腳本加載完畢的時間有兩種情況:
情況1: HTML 還沒有被解析完的時候,async腳本已經加載完了,那么 HTML 停止解析,去執行腳本,腳本執行完畢后觸發DOMContentLoaded事件。如下圖所示:
情況2: HTML 解析完了之后,async腳本才加載完,然后再執行腳本,那么在HTML解析完畢、async腳本還沒加載完的時候就觸發DOMContentLoaded事件。如下圖所示:
總之, DomContentLoaded 事件只關注 HTML 是否被解析完,而不關注 async 腳本。
defer
如果 script 標簽中包含 defer,那么這一塊腳本將不會影響 HTML 文檔的解析,而是等到 HTML 解析完成后才會執行。而 DOMContentLoaded 只有在 defer 腳本執行結束后才會被觸發。
defer腳本同樣包含兩種情況:
情況1:HTML還沒解析完成時,defer腳本已經加載完畢,那么defer腳本將等待HTML解析完成后再執行。defer腳本執行完畢后觸發DOMContentLoaded事件。如下圖所示
情況2:HTML解析完成時,defer腳本還沒加載完畢,那么defer腳本繼續加載,加載完成后直接執行,執行完畢后觸發DOMContentLoaded事件。如下圖所示:
注意defer情況2與async情況2的兩個圖非常相似,區別就在於DOMContentLoaded事件的觸發時間點。
對於defer腳本,《JavaScript高級程序設計》一書的說法是:“按照h5規范,兩個defer腳本會安裝它們出現的先后順序執行,兩個腳本會在DOMContentLoaded之前執行。”這和我們上面的分析一致。然而,該書接下來說,“但事實上,defer腳本不一定會按順序執行,也不一定會在DOMContentLoaded之前執行。”這是一個待再繼續研究測試的問題
參考博客
Using setTimeout to speed up window.onload: https://mathiasbynens.be/notes/settimeout-onload
http://www.cnblogs.com/coco1s/p/4010310.html
你不知道的DOMContentLoaded:https://zhuanlan.zhihu.com/p/25876048
https://www.cnblogs.com/lhb25/p/how-browsers-work.html
原文:https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/