作了好幾年前端了,最近想進個大廠,參加了阿里、百度、今日頭條的面試,被完全的打擊了,感受本身的基礎不是很牢,因此萌發了重學前端的想法,向習覺得常的知識/技能,多問一個爲何,多瞭解一下原理,或者源碼javascript
今天咱們來聊聊html中資源加載和執行的順序問題。php
作了幾年前端了,但是當瀏覽器獲取到html文件後怎麼加載執行文件,這些問題你能回答嗎css
爲了搞清楚這些問題,咱們來作下試驗,我用的是chrome瀏覽器。html
咱們先構造一個html文件,在head裏面交替加載10個css文件和10個js文件,這幾個文件地址是我用本地php服務建立的,都延遲3秒返回數據,以便咱們清楚觀察效果前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="http://test.com/test/css1">
<script src="http://test.com/test/js1"></script>
<link rel="stylesheet" href="http://test.com/test/css2">
<script src="http://test.com/test/js2"></script>
<link rel="stylesheet" href="http://test.com/test/css3">
<script src="http://test.com/test/js3"></script>
<link rel="stylesheet" href="http://test.com/test/css4">
<script src="http://test.com/test/js4"></script>
<link rel="stylesheet" href="http://test.com/test/css5">
<script src="http://test.com/test/js5"></script>
<link rel="stylesheet" href="http://test.com/test/css6">
<script src="http://test.com/test/js6"></script>
<link rel="stylesheet" href="http://test.com/test/css7">
<script src="http://test.com/test/js7"></script>
<link rel="stylesheet" href="http://test.com/test/css8">
<script src="http://test.com/test/js8"></script>
<link rel="stylesheet" href="http://test.com/test/css9">
<script src="http://test.com/test/js9"></script>
<link rel="stylesheet" href="http://test.com/test/css10">
<script src="http://test.com/test/js10"></script>
</head>
<body>
</body>
</html>
複製代碼
css和js的地址我是使用php框架構造的,目的是爲了使用php的sleep延遲返回資源,好讓咱們更清楚的看到時序,你能夠經過node或其餘後臺自行構造java
//js文件,等待3s後返回
public function actionJs1(){
sleep(3);
return "console.log('js1111111')";
}
//css文件,等待3s返回
public function actionCss1(){
sleep(3);
}
複製代碼
咱們經過chrome來看下加載時序,能夠看到,瀏覽器首先下載html文件,下載以後緊接着按照文檔順序請求css一、js一、css二、js二、css三、js3,能夠看到瀏覽器一次能夠加載6個文件,加載完成以後再加載6個,直到所有加載完成。node
而加載順序實際反覆測試幾回,均爲【css一、js一、css二、js二、css三、js3】、【css四、css五、css六、css七、css八、css9】、【css十、js四、js五、js六、js七、js8】、【js九、js10】,能夠看出,有兩個特色:總體來講是按照文檔在html中出現順序進行加載的,可是會部分優先加載css文件css3
爲了證明上面的猜測,有對html代碼進行了改造,10個css全放前面,10個js全放後面面試
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css10">
<script src="http://test.com/test/js1"></script>
......
<script src="http://test.com/test/js10"></script>
複製代碼
果真,瀏覽器優先把前面10個css先加載完畢,而後才加載js chrome
而若是咱們把10個js放到10個css文件前面呢
<script src="http://test.com/test/js1"></script>
......
<script src="http://test.com/test/js10"></script>
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css10">
複製代碼
能夠看到,瀏覽器先加載了6個js,而後又優先把10個css加載完,以後才加載js
接下來在body中再添加10個圖片,看看css、js、圖片,這三種資源的加載順序,不用測試咱們應該也能想到,圖片應該在css和js以後,實驗驗證一下。
構造測試html,每一個資源設置6個,順序以下
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css6">
<img src="http://test.com/test/img1"/>
......
<img src="http://test.com/test/img6"/>
<script src="http://test.com/test/js1"></script>
......
<script src="http://test.com/test/js6"></script>
複製代碼
chrome文件加載時序爲,先加載6個css、再加載6個js,最後加載6個圖片
瀏覽器每次只能加載6個文件,那還能不能再多加載一些呢,假如加載的資源域名不一樣,會發生什麼?
//前六個css域名爲test.com
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css6">
//後六個css域名爲m.test.com
<link rel="stylesheet" href="http://m.test.com/test/css1">
......
<link rel="stylesheet" href="http://m.test.com/test/css6">
複製代碼
能夠看到,瀏覽器一次性加載了12個文件
1.瀏覽器加載文件,總體順序是按照文件在html中出現的順序進行加載
2.可是會優先加載css、而後加載js、最後加載圖片
3.同一個域名的資源,谷歌瀏覽器每次加載6個,不一樣域名的資源能夠並行加載
看到這裏,咱們應該會性能優化有了一些想法了吧,好比使用不一樣域名能夠提升總體加載速度
js會在加載成功當即執行嗎,仍是會受到別的因素影響呢?咱們先來測試一下吧,假如css延遲3s返回,js取消延遲當即返回,看看會發生什麼
//css延遲3s返回數據
<link rel="stylesheet" href="http://test.com/test/css1">
//js當即返回,console.log('js1')
<script src="http://test.com/test/js1"></script>
複製代碼
刷新頁面能夠看到儘管js很快就加載完成了,可是並無當即執行,等了3s左右才執行,也就是在css加載完成後才執行,即css的加載會阻塞js的執行,那是否是由於css在js前面的緣由呢?
咱們調換一下css和js的順序,則能夠看到js當即打印,並不會等待css的加載
//js當即返回,console.log('js1')
<script src="http://test.com/test/js1"></script>
//css延遲3s返回數據
<link rel="stylesheet" href="http://test.com/test/css1">
複製代碼
接下來咱們看看兩個js的加載執行順序,先從最普通的開始
//js1延遲5s返回數據,console.log('js1')
<script src="http://test.com/test/js1"></script>
//js2延遲3s返回數據,console.log('js2')
<script src="http://test.com/test/js2"></script>
<script> console.log('js3') </script>
複製代碼
等待5s後,控制檯前後打印 js1 js2 js3,雖然js2提早加載完成,可是仍然要等待前面的js1加載執行完畢才能執行
js異步加載執行有兩種方式async和defer,接下來咱們給js添加async屬性看看
//js1延遲5s返回數據,console.log('js1')
<script src="http://test.com/test/js1" async></script>
//js2延遲3s返回數據,console.log('js2')
<script src="http://test.com/test/js2" async></script>
<script> console.log('js3') </script>
複製代碼
控制檯當即打印順序爲
js3
js2
js1
複製代碼
可見添加async屬性js不會阻塞其後面js的執行,誰先加載完成誰先執行,接下來添加defer屬性。
//js1延遲5s返回數據,console.log('js1')
<script src="http://test.com/test/js1" defer></script>
//js2延遲3s返回數據,console.log('js2')
<script src="http://test.com/test/js2" defer></script>
<script> console.log('js3') </script>
複製代碼
控制檯打印順序以下,能夠看出,添加defer屬性的js,不會阻塞後面的js執行,可是多個添加defer的js,仍然按照既有順序執行。
js3
js1
js2
複製代碼
下面是一張經典js加載執行時機對比圖,
瀏覽器遇到沒有添加異步屬性的js,會當即加載並執行,也就是說會阻塞html的解析
瀏覽器遇到添加async屬性的js會當即加載(固然了,如谷歌瀏覽器,就算沒有添加async,它也會提早識別html中的js文件進行加載,加載時機如第一部分討論),並在js加載完畢以後當即執行;多個async屬性的js誰先加載完成誰先執行
瀏覽器遇到添加defer屬性的js會當即加載,可是不會當即執行,而是會在html解析完成以後,DOMContentLoaded觸發以前執行;多個defer屬性的js會按照文檔順序執行
上面提到添加defer屬性的js會在文檔解析以後,DOMContentLoaded觸發以前執行,也就是說,在添加defer屬性的js中咱們是能夠獲取到dom的
//js1延遲5s返回數據,console.log(document.getElementById('test'))
<script src="http://test.com/test/js1" defer></script>
<script> console.log(document.getElementById('test')) </script>
<div id="test"></div>
複製代碼
內聯js,在id=「test」的dom以前,因此輸出null,而defer屬性的js打印出了dom
null
<div id="test"></div>
複製代碼
即在defer屬性的js中,能夠安全的操做dom
網上查看一些資料,通常說法是這樣的
一、當 onload 事件觸發時,頁面上全部的DOM,樣式表,腳本,圖片,flash都已經加載完成了。
二、當 DOMContentLoaded事件觸發時,僅當DOM加載完成,不包括樣式表,圖片,flash。
可是經過測試發現並不是如此,假設css延遲3s返回,圖片延遲5s返回,咱們看看打印
//css延遲3s返回數據
<link rel="stylesheet" href="http://test.com/test/css1">
<script> document.addEventListener('DOMContentLoaded',function () { console.log('DOMContentLoaded') },false) window.onload=function () { console.log("onload") } </script>
//延遲5s返回數據
<img src="http://3w.com/test/img1"/>
複製代碼
能夠看出3s以後纔打印DOMContentLoaded,5s以後打印onload,也就是說DOMContentLoaded是須要等待css加載完成的 咱們剛纔前面說了,defer屬性的js要在DOMContentLoaded以前執行,那麼假如defer屬性延遲3s返回呢,咱們看看效果
<script> document.addEventListener('DOMContentLoaded',function () { console.log('DOMContentLoaded') },false) window.onload=function () { console.log("onload") } </script>
//js1延遲3s返回數據
<script src="http://test.com/test/js1" defer></script>
//延遲5s返回數據
<img src="http://3w.com/test/img1"/>
複製代碼
一樣的也在3s以後打印DOMContentLoaded,5s以後打印onload,能夠說網上的結論和實際是不相符的
一樣的這些結論也給咱們作前端優化提供一些思路,好比給js添加async屬性、減小js及css大小、使用懶加載減小圖片請求,使其儘快進入onload事件等等
有興趣的同窗歡迎關注公衆號,讓咱們一塊兒重學前端,夯實基礎。