js延遲(異步)加載的6種方式


為什么要延遲加載js呢?

  對於js的優化(關於js的延遲加載)的好處是有助於提高頁面加載速度js延遲加載就是等頁面加載完成之后在加載js文件

  之所以要優化是因為HTML元素是按其在頁面中出現的次序調用的,如果用javascript來管理頁面上的元素(使用文檔對象模型dom),並且js加載於欲操作的HTML元素之前,則代碼將出錯。也就是說,我們寫了js語句來獲取DOM對象,但由於DOM結構還沒有加載完成,因此獲取到的是空對象

 

舉個栗子吧~

<head>
    <script type="text/javascript">
        var ul = document.getElementsByTagName('ul')[0]; //獲取ul
        var list = ul.getElementsByTagName('li');
        for(var i =0;i<list.length;i++){
            ul.appendChild(document.createElement('li'));
        }
    </script>
</head>
<body>
<ul>
    <li>111</li>
    <li>222</li>
    <li>333</li>
</ul>
</body>
</html>

你平時好像就是這么寫的,但是你會發現控制台會報錯:Uncaught TypeError :Cannot read property 'getElementByTagName' of undefined at... 

這就是因為js加載執行於DOM結構之前,所以獲取不到。簡單的解決辦法是把<script>放在<body>后面。

 

js的同步加載和異步加載

 同步加載,又稱阻塞模式,是我們平時使用最多的方式,也就是直接將<script>寫在<head>里。這種方式會阻止瀏覽器的后續處理,停止后續的解析,直到當前的加載完成。一般來說,同步加載是安全的,但如果我們js里設計到document內容輸出、獲取或修改DOM結構等行為,就會產生頁面阻塞代碼出錯。所以一般就會建議把<script>寫在頁面最底部,以減少頁面阻塞。(這種方式可能也是我們剛開始接觸到js優化,最常使用的一種方式。)

  異步加載,又稱為非阻塞加載,在瀏覽器下載執行js的同時,還會繼續后續頁面的處理。這里也是一般面試會問到的一點,即js延遲加載的方式有哪些?

 

js延遲加載的六種方式

  一般有六種方式;defer屬性、async屬性、動態創建dom方式、使用jquery的getScript方法、使用setTimeout延遲方法、讓js最后加載。

  寫的是六種方式,實際上自己在項目中真實用到的也就是讓js最后加載。所以對這所謂的六種方式,可能僅作為一種知識儲備,當以后的項目有這種問題需求了,可以有不同的解決思路。

一、defer屬性

HTML 4.01為 <script>標簽定義了defer屬性(延遲腳本的執行)。

其用途是:表明腳本在執行時不會影響頁面的構造,瀏覽器會立即下載,但延遲執行,即腳本會被延遲到整個頁面都解析完畢之后再執行

defer屬性只適用於外部腳本文件,只有 Internet Explorer 支持 defer 屬性

並且defer屬性解決了async引起的腳本順序問題,使用defer屬性,腳本將按照在頁面中出現的順序加載和運行。

示例1:

//腳本1
<script defer src="js/vendor/jquery.js"></script>
//腳本2
<script defer src="js/script2.js"></script>
//腳本3
<script defer src="js/script3.js"></script>​

 上述代碼添加 defer 屬性,腳本將按照在頁面中出現的順序加載,因此可確保腳本1必定加載於腳本2和 腳本3之前,同時腳本2必定加載於腳本3之前。(補充:最近在看《高級程序設計》里面說,實際上延遲腳本並不一定會按照順序執行,也不一定會在DOMContenterLoaded事件觸發前執行,因此最好只包含一個延遲腳本

示例2:

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>defer練習</title>
</head>
<script type="text/javascript" defer="defer" src="example1.js"></script>
<script type="text/javascript" defer="defer" src="example2.js"></script>
<body>
<!--這里是內容-->
</body>
</html>

 

在上面的例子中,雖然把<script>放在里<head>里,但是包含的腳本會延遲到整個頁面之后,即延遲到瀏覽器遇到</html>標簽后再執行。同時,在XHTML文檔中,要把defer屬性設置為defer="defer"

二、async屬性

HTML 5為 <script>標簽定義了async屬性。添加此屬性后,腳本和HTML將一並加載(異步),代碼將順利運行

瀏覽器遇到async腳本時不會阻塞頁面渲染,而是直接下載然后運行。但這樣的問題是,不同腳本運行次序就無法控制,只是腳本不會阻止剩余頁面的顯示

async屬性只適用於外部腳本文件。

示例:

//腳本1
<script async src="js/vendor/jquery.js"></script>
//腳本2
<script async src="js/script2.js"></script>
//腳本3
<script async src="js/script3.js"></script>​

上述代碼添加async 屬性,這三者的調用順序是不確定的,腳本1可以在腳本2和腳本3之前會之后調用,這是完全不確定的。如果腳本2和腳本3需要依賴腳本1中的函數,那么不確定的調用順序會導致錯誤。(補充:異步腳本一定會在頁面的load事件前執行,但可能會在DOMContenterLoaded事件觸發之前或之后執行)

所以,當頁面的不同腳本之間彼此獨立,且不依賴於本頁面的其他任何腳本時,async是最理想的選擇

總結:defer和async的異同點

相同:

  • 加載文件時不會阻塞頁面渲染
  • 對於內部的js不起作用
  • 使用這兩個屬性的腳本中不能調用document.write方法

區別:

  • 如果腳本無需等待頁面解析,且無依賴獨立運行,那么應使用 async。也就是每一個async屬性的腳本都在它下載結束之后立即執行,同時會在window的load事件之前執行。
  • 如果腳本需要等待解析,且依賴於其它腳本,調用這些腳本時應使用 defer,將關聯的腳本按所需順序置於 HTML 中。

三、動態創建DOM方式

//這些代碼應被放置在</body>標簽前(接近HTML文件底部)
<script type="text/javascript">
function downloadJSAtOnload(){
    var element = document.createElement("script");
    element.src = "defer.js";
    document.body.appendChild(element); 
}
if (window.addEventListener) //添加監聽事件
    window.addEventListener("load",downloadJSAtOnload,false);   //事件在冒泡階段執行
else if (window.attachEvent) 
    window.attachEvent("onload",downloadJSAtOnload);
else 
    window.onload =downloadJSAtOnload;
</script>

四、使用jquery的getScript方法

getScript() 方法通過 HTTP GET 請求載入並執行 JavaScript 文件。

  語法:jQuery.getScript(url,success(response,status))

  url(必寫):將要請求的 URL 字符串
  success(response,status)(可選):規定請求成功后執行的回調函數。
  其中的參數
  response - 包含來自請求的結果數據
  status - 包含請求的狀態("success", "notmodified", "error", "timeout" 或 "parsererror")

//加載並執行 test.js:
$.getScript("test.js");
//加載並執行 test.js ,成功后顯示信息
$.getScript("test.js", function(){
  alert("Script loaded and executed.");
});

五、使用setTimeout延遲方法

<script type="text/javascript">
  function A(){
    $.post("/lord/login",{name:username,pwd:password},function(){
      alert("Hello World!");
    })
  }
  $(function (){
    setTimeout("A()",1000); //延遲1秒
  })
</script>

六、讓js最后加載

   將腳本元素放在文檔體的底端(</body>標簽前面),這樣腳本就可以在HTML解析完畢后加載了。但此方案的問題是,只有在所有HTML DOM加載完成后才開始腳本的加載/解析過程。對於有大量js代碼的大型網站,可能會帶來顯著的性能損耗。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM