重學前端:作了這麼多年前端,你真的瞭解html中資源加載執行時序嗎?

作了好幾年前端了,最近想進個大廠,參加了阿里、百度、今日頭條的面試,被完全的打擊了,感受本身的基礎不是很牢,因此萌發了重學前端的想法,向習覺得常的知識/技能,多問一個爲何,多瞭解一下原理,或者源碼javascript

今天咱們來聊聊html中資源加載和執行的順序問題。php

作了幾年前端了,但是當瀏覽器獲取到html文件後怎麼加載執行文件,這些問題你能回答嗎css

  • 按照什麼順序加載文件?
  • 每次能加載多少個文件?
  • css沒加載完會執行js嗎?
  • async和defer的區別是什麼?
  • DOMContentLoaded事件和onload事件,什麼時間觸發?

爲了搞清楚這些問題,咱們來作下試驗,我用的是chrome瀏覽器。html

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個,不一樣域名的資源能夠並行加載

看到這裏,咱們應該會性能優化有了一些想法了吧,好比使用不一樣域名能夠提升總體加載速度

html中js執行時機

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">
複製代碼

結論

  1. 若是css在js前面,則css的加載會阻塞js的執行,即css加載完畢後,執行js
  2. 若是css在js後面,則不會阻塞js的執行

接下來咱們看看兩個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

結論

  1. 若是css在js前面,則css的加載會阻塞js的執行,即css加載完畢後,執行js
  2. 若是css在js後面,則不會阻塞js的執行
  3. 非異步js按照順序進行執行
  4. 多個async屬性的js,誰先加載完成誰執行
  5. defer屬性的js在文檔解析以後,DOMContentLoaded觸發以前執行,多個defer屬性的js按文檔順序執行

DOMContentLoaded和onload

網上查看一些資料,通常說法是這樣的

一、當 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,能夠說網上的結論和實際是不相符的

結論

  1. 當 頁面上全部的DOM,樣式表,腳本,圖片,flash都加載完成以後觸發onload。
  2. 當DOM加載解析完成、css加載完成、內聯js執行完成、defer屬性的js完成纔會觸發DOMContentLoaded,圖片和async屬性的js,不會阻止發DOMContentLoaded的加載。

一樣的這些結論也給咱們作前端優化提供一些思路,好比給js添加async屬性、減小js及css大小、使用懶加載減小圖片請求,使其儘快進入onload事件等等

有興趣的同窗歡迎關注公衆號,讓咱們一塊兒重學前端,夯實基礎。

相關文章
相關標籤/搜索