本文首發在github,感興趣請點擊此處javascript
window 的 onload 事件對於前端童鞋來講確定是熟的不能再熟了,相信你們在剛入門時,見的最多的可能就是 load 事件了。load 事件接觸多了,你們就會接觸到它的閨蜜 DOMContentLoaded 事件,網上有不少介紹這兩個事件的文章,對們它的解釋無外乎如下兩種css
MDN的解釋:load 應該僅用於檢測一個徹底加載的頁面 當一個資源及其依賴資源已完成加載時,將觸發load事件。html
意思是頁面的html、css、js、圖片等資源都已經加載完以後纔會觸發 load 事件。前端
MDN的解釋:當初始的 HTML 文檔被徹底加載和解析完成以後,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。java
意思是HTML下載、解析完畢以後就觸發。git
看了這兩個解釋,我仍然一臉懵逼,只是像小學生背課文同樣知道 load 和 DOMContentLoaded 事件的觸發時機,但仍是不明白究竟什麼狀況下觸發這兩種事件。github
這兩個詞語表達的是一個意思,就是瀏覽器將資源下載到本地的過程。chrome
解析的意思是將一個元素經過必定的方式轉換成另外一種形式。 好比 html 的解析。首先要明確,html 下載到瀏覽器的表現形式就是 包含字符串的文件。瀏覽器將 html 文件裏面的字符串讀取到內存中,按照 html 規則,對字符串進行取詞編譯,將字符串轉化成另外一種易於表達的數據結構。咱們看下一段代碼:瀏覽器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>只有css</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<div id="div1"></div>
<link rel="stylesheet" href="./c1.css" />
<link rel="stylesheet" href="./c3.css" />
<script src="http://test.com:9000/mine/load/case2/j1.js "></script>
<link rel="stylesheet" href="./c4.css" />
<div id="div2"></div>
</body>
</html>
複製代碼
瀏覽器會對這個 html 文件進行編譯,轉化成相似下面的結構(這裏把 head 中的其餘標籤省略了)。數據結構
瀏覽器會對轉化後的數據結構自上而下進行分析:首先開啓下載線程,對全部的資源進行優先級排序下載(注意,這裏僅僅是下載)。同時主線程會對文檔進行解析:
有一點要注意的是,在 body 中第一個 script 資源下載完成以前,瀏覽器會進行首次渲染,將該 script 標籤前面的 DOM 樹和 CSSOM 合併成一棵 Render 樹,渲染到頁面中。這是頁面從白屏到首次渲染的時間節點,比較關鍵。
DOM 構建的意思是,將文檔中的全部 DOM 元素構建成一個樹型結構。
注意,DOM 構建是自上而下進行構建的,會受到 js 執行的干擾。
將文檔中的全部 css 資源合併。
將 DOM 樹和 CSS 合併成一棵渲染樹,render 樹在合適的時機會被渲染到頁面中。(好比遇到 script 時, 該 script 尚未下載到本地時)。
當咱們輸入一個頁面地址時,發生了哪些事情呢?
body 裏的狀況比較多,body 裏可能只有 DOM 元素,可能既有 DOM、也有 css、js 等資源,js 資源又有可能異步加載 圖片、css、js 等。DOM 結構不一樣,瀏覽器的解析機制也不一樣,咱們分開來討論。
測試代碼以下
<body>
<!-- 白屏 -->
<div id="div1"></div>
<!-- 白屏 -->
<link rel="stylesheet" href="./c1.css" />
<!-- 白屏 -->
<link rel="stylesheet" href="./c3.css" />
<!-- 若是此時 j1.js 還沒有下載到本地,則首次渲染,此時的 DOM 樹 只有 div1 ,因此頁面上只會顯示 div1,樣式是 c1.css 和 c3.css 的並集。-->
<!-- 若是此時 j1.js 已經下載到本地,則先執行 j1.js,頁面不會渲染,因此此時仍然是白屏。-->
<!--下面的 js 阻塞了 DOM 樹的構建,因此下面的 div2 沒有在文檔的 DOM 樹中。 -->
<script src="http://test.com:9000/mine/load/case2/j1.js "></script>
<!-- j1.js 執行完畢,繼續 DOM 解析,div2 被構建在文檔 DOM 樹中,此時頁面上有了div2 元素,樣式仍然是 c1.css 和 c3.css 的並集 -->
<link rel="stylesheet" href="./c4.css" />
<!-- c4.css 加載完畢,從新構建render樹,樣式變成了 c1.css、c3.css 和 c4.css 的並集 -->
<div id="div2"></div>
<script> // 利用 performance 統計 load 加載時間。 window.onload=function(){console.log(performance.timing.loadEventStart - performance.timing.fetchStart);} </script>
</body>
複製代碼
你們能夠調整資源擺放位置,觀察瀏覽器的解析表現。
上面只是講了 html 文檔的加載與渲染,並無講 DOMContentLoaded 事件的觸發時機。直截了當地結論是,DOMContentLoaded 事件在 html文檔加載完畢,而且 html 所引用的內聯 js、以及外鏈 js 的同步代碼都執行完畢後觸發。
你們能夠本身寫一下測試代碼,分別引用內聯 js 和外鏈 js 進行測試。
當頁面 DOM 結構中的 js、css、圖片,以及 js 異步加載的 js、css 、圖片都加載完成以後,纔會觸發 load 事件。
注意:
你們能夠在 chrome 中試一下。
這裏要注意關鍵詞:同一域名。若是 n 個不一樣域名的話,在瀏覽器設置的最大併發上限之內(默認是10個),是能夠達到 n * 6 個的最大併發的下載的。
另外,load 事件與 DOMContentLoaded 事件觸發所花費的時間,能夠利用 performance 這個對象的一些屬性進行統計,時間精確到納秒級。一些大公司的性能統計也主要利用這個對象的數據進行上報。
還有一些其餘的性能統計屬性,你們能夠研究下。
以上內容但願能給你們帶來不同的思考,並但願能幫到你們理解文檔的加載機理。