異步加載的幾種方式

繼上一篇“淺談js加載的時間線”下面來講一下關(guān)于異步加載的幾種方式

第一種延遲腳本defer

HTML4.01為<script>標(biāo)簽定義了defer屬性,這個屬性的用途是表明腳本在執(zhí)行時不會影響頁面的構(gòu)造,也就是說,腳本會被延遲到整個頁面都解析完畢后再運行,因此在<script>元素中設(shè)置defer屬性,相當(dāng)于告訴瀏覽器立即下載,但是延遲執(zhí)行

例如:在下面代碼中將外部js放到<head>里,然后設(shè)置defer="defer"屬性

<!DOCTYPE html>
<html lang="en"  xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>PISMDB</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <!--&lt;!&ndash; Fonts&ndash;&gt;-->
    <link rel="stylesheet" type="text/css" href="./assets/css/reset.css" th:href="@{/static/assets/css/reset.css}">
    <!--&lt;!&ndash; Vendors&ndash;&gt;-->
    <link rel="stylesheet" type="text/css" href="./assets/vendors/bootstrap/bootstrap.min.css"
          th:href="@{/static/assets/vendors/bootstrap/bootstrap.min.css}">
    <!-- css -->
    <link rel="stylesheet" type="text/css" href="./assets/css/main.css" th:href="@{/static/assets/css/main.css}">
    <script src="./assets/vendors/_jquery/jquery-3.3.1.min.js" defer="defer"></script>
    <script type="text/javascript" src="./assets/vendors/_jquery/jquery.SuperSlide.2.1.3.js" defer="defer"></script>
    <script src="./assets/vendors/bootstrap/bootstrap.min.js" defer="defer"></script>
</head>
<body>
<!-- 定義頭部 -->
<div class="head">
...

在瀏覽器中執(zhí)行代碼通過network可查看,js的加載是按照順序來進行加載。

image.png

然后查看js的執(zhí)行順序
添加代碼:readyState代表dcument的狀態(tài),監(jiān)聽在文檔加載時的狀態(tài)變化,DOMContentLoaded事件在文檔解析完成后的某個時間進行觸發(fā)

 <script type="text/javascript">
     console.log(document.readyState);
        document.onreadystatechange = function(){
            console.log(document.readyState);
        }
        console.log("這是js執(zhí)行的");
         document.addEventListener('DOMContentLoaded',function(){
            console.log('DOMContentLoaded 觸發(fā)了');
        },false)

瀏覽器執(zhí)行結(jié)果如下:js在DOMContentLoader觸發(fā)之前進行加載


image.png

在上面例子中,雖然我們把<script>元素放到了文檔的<head>元素中,在此處進行加載,但是執(zhí)行卻延遲到瀏覽器遇到</html>標(biāo)簽后再執(zhí)行,HTML5規(guī)范要求腳本按照他們出現(xiàn)的先后順序執(zhí)行,因此第一個延遲腳本會先于第二個延遲腳本執(zhí)行,第二個先于第三個延遲腳本執(zhí)行,而這三個腳本會先于DOMContentLoaded事件執(zhí)行。

在現(xiàn)實當(dāng)中,延遲腳本并不一定會按照順序執(zhí)行,也不一定會在DOMContentLoaded事件觸發(fā)前執(zhí)行,因此最好只包含一個延遲腳本

defer屬性只適用于外部腳本文件,這一點在HTML5中已經(jīng)明確規(guī)定,因此支持HTML5的實現(xiàn)會忽略給嵌入腳本設(shè)置的defer屬性。IE4~IE7還支持對嵌入腳本的defer屬性,但是IE8及之后的版本則完全支持HTML5規(guī)定的行為,因此將延遲腳本放在頁面底部是最佳選擇

第二種HTML5為<script>元素定義的async屬性

這個屬性與defer屬性類似,都用于改變處理腳本的行為,同樣與defer類似,只適用于外部腳本文件,并告訴瀏覽器立即下載文件,但與defer不同的是,標(biāo)記為async的腳本并不保證按照指定它們的先后順序執(zhí)行
例如:將外部js腳本設(shè)置為async屬性

<!DOCTYPE html>
<html lang="en"  xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>PISMDB</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <!--&lt;!&ndash; Fonts&ndash;&gt;-->
    <link rel="stylesheet" type="text/css" href="./assets/css/reset.css" th:href="@{/static/assets/css/reset.css}">
    <!--&lt;!&ndash; Vendors&ndash;&gt;-->
    <link rel="stylesheet" type="text/css" href="./assets/vendors/bootstrap/bootstrap.min.css"
          th:href="@{/static/assets/vendors/bootstrap/bootstrap.min.css}">
    <!-- css -->
    <link rel="stylesheet" type="text/css" href="./assets/css/main.css" th:href="@{/static/assets/css/main.css}">
    <script src="./assets/vendors/_jquery/jquery-3.3.1.min.js" async></script>
    <script type="text/javascript" src="./assets/vendors/_jquery/jquery.SuperSlide.2.1.3.js" async></script>
     <script src="./assets/vendors/bootstrap/bootstrap.min.js" async></script> 
     <!--  -->
</head>

在以上代碼中,第二個腳本文件可能會在第一個腳本文件之前執(zhí)行,因此確保它們之間互不依賴非常重要
異步腳本一定會在頁面的load事件之前執(zhí)行,但可能會在DOMContentLoaded事件觸發(fā)之前或者之后執(zhí)行。

第三種動態(tài)的創(chuàng)建script的標(biāo)簽(可以解決兼容h5以及低版本ie的問題)

<script type="text/javascript">
            function asyncLoaded(url,callback){
                var script = document.createElement("script");
//                script.src = url;   假如說網(wǎng)速非常好,直接執(zhí)行完成了,后面就監(jiān)聽不到狀態(tài)的改變了
                if(script.readyState){
                    script.onreadystatechange = function(){
                        if(script.readyState == "complete" || script.readyState =="loaded"){
//                            執(zhí)行某個函數(shù)
                            callback()
                        }
                    }
                }else{
                    script.onload = function(){
//                        執(zhí)行某個函數(shù)
                        callback()
                    }
                }
                script.src = url;    //異步的過程
                document.head.appendChild(script)    
            }
            asyncLoaded("05.js",function(){
                fn()          //05.js中的函數(shù)
            })
        </script>

這種方式的缺點就是要清楚文件的加載順序,當(dāng)js文件多了,依賴關(guān)系復(fù)雜的時候,很難管理加載的依賴順序

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容