關於js延遲加載(異步操做)的方式 addEventListener與attachEvent

1、概述

   最近從新開始學習js,在第一章的一個小節裏寫到了「腳本調用策略」,書上寫的這部分很少,可是發如今我以前的(筆)面試中,問到的頻率仍是比較高的。本身一直習慣於直接把全部js文件寫在head裏,後來瞭解到優化後,會把js放在最底部,但並不太懂這樣作的好處,並且其餘的一些處理方式,本身也並未有過實際的操做。javascript

  在面試中對這部分的考察,主要考察的是程序的性能方面。程序的性能是一個項目不斷地追求的,一般也是項目完成後須要長期作的一件事情,爲了讓用戶的體驗更好。性能優化的核心思想就是快,能夠預先準備數據(如緩存的使用),能夠按需獲取,能夠分段獲取等都是常見的優化手段。html

  而對於js的優化(關於js的延遲加載)的好處是有助於提升頁面加載速度,js延遲加載就是等頁面加載完成以後在加載js文件。java

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

示例:面試

<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>

上面的代碼猛一看好像沒啥問題,由於咱們老是很習慣這樣作,可是運行後發現控制檯報錯。瀏覽器

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

 

2、js的同步加載和異步加載

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

  異步加載,又稱爲非阻塞加載,在瀏覽器下載執行js的同時,還會繼續後續頁面的處理。這裏也是通常面試會問到的一點,即js延遲加載的方式有哪些?性能優化

3、js延遲加載的六種方式

  通常有六種方式;defer屬性、async屬性、動態建立dom方式、使用jquery的getScript方法、使用setTimeout延遲方法、讓js最後加載。app

  寫的是六種方式,實際上本身在項目中真實用到的也就是讓js最後加載。因此對這所謂的六種方式,可能僅做爲一種知識儲備,當之後的項目有這種問題需求了,能夠有不一樣的解決思路。

(一)defer屬性

HTML 4.01爲 <script>標籤訂義了defer屬性(延遲腳本的執行)。

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

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

而且defer屬性解決了async引發的腳本順序問題(見async的缺點),使用defer屬性,腳本將按照在頁面中出現的順序加載和運行。

示例:

//腳本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以前。

(二)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中的函數,那麼不肯定的調用順序會致使錯誤。

因此,當頁面的不一樣腳本之間彼此獨立,且不依賴於本頁面的其餘任何腳本時,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>

 

補充知識點:addEventListener與attachEvent

(四)使用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延遲方法

  目的:延遲加載js代碼,給網頁加載留出時間

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

(六)讓js最後加載

   將腳本元素放在文檔體的低端,這樣腳本就能夠在HTML解析完畢後加載了。但此方案的問題是,只有在全部HTML DOM加載完成後纔開始腳本的加載/解析過程。對於有大量js代碼的大型網站,可能會帶來顯著的性能損耗。

相關文章
相關標籤/搜索