defer和async的原理与区别
上一篇剛轉載了一篇有關于網站性能優化的文章,其中提及到了頁面的加載和渲染的過程,提到了defer和async的相關區別,但是本人在此之前并沒有深究其中的區別。
defer和async是script標簽的兩個屬性,用于在不阻塞頁面文檔解析的前提下,控制腳本的下載和執行。
在介紹他們之前,我們有必要先了解一下頁面的加載和渲染過程:
在這個過程中,腳本文件的下載和執行是與文檔解析同步進行,也就是說,它會阻塞文檔的解析,如果控制得不好,在用戶體驗上就會造成一定程度的影響。
所以我們需要清楚的了解和使用defer和async來控制外部腳本的執行。
先來簡明概要的介紹一下下面三者的區別:
沒有 defer 或 async,瀏覽器會立即加載并執行指定的腳本,“立即”指的是在渲染該 script 標簽之下的文檔元素之前,也就是說不等待后續載入的文檔元素,讀到就加載并執行。
有 async,加載和渲染后續文檔元素的過程將和 script.js 的加載與執行并行進行(異步)
有 defer,加載后續文檔元素的過程將和 script.js 的加載并行進行(異步),但是 script.js 的執行要在所有元素解析完成之后,DOMContentLoaded 事件觸發之前完成。
接著,我們來看一張圖:
藍色線代表網絡讀取,紅色線代表執行時間,這倆都是針對腳本的;綠色線代表 HTML 解析。
此圖告訴我們以下幾個要點:
下面為了演示腳本的執行情況,借鑒了詳解defer和async原理及應用
為了演示腳本的執行情況,進而介紹這兩個屬性的作用,我們先來搭建一個簡單的服務器:
創建了一個簡易的Node服務器server.js,其代碼如下:
從上面的代碼我們可以看出,當服務器接收到請求之后,會判斷請求資源是否為JS,如果是則延遲1s后返回對應的資源。
啟動這個服務很簡單,只需執行node server.js即可,然后就可以在瀏覽器中輸入http://localhost:3000/app/index.html訪問主頁了
現在我們來看看index.html中的內容:
在這個HTML文檔中,我們先在head中引用了一個外部的腳本文件js/1.js,然后在我們要顯示的Hello World后面又引用了一個js/2.js,它們的內容都很簡單,就是彈出對應的標示信息:
// js/1.js alert(1);// js/2.js alert(2);下面我們就來訪問主頁,看看會發生些什么:
從圖中可以看到,渲染的過程的確是自上而下,同步進行的,也就是說遇到外部的腳本,就得暫停文檔的解析,下載并且解釋執行,這種方式是阻塞的,會造成網頁空白的現象。
現在稍微修改一下代碼,將head中的script標簽加上defer屬性,然后也稍微改動一下兩個JS文件:
<!DOCTYPE html> <html><head><title>defer & async</title><link rel="stylesheet" type="text/css" href="css/main.css"><!-- adding a 'defer' attribute, by default, the value will be 'true' --><script type="text/javascript" src="js/1.js" defer></script></head><body><div class="text">Hello World</div><script type="text/javascript" src="js/2.js"></script></body> </html> // js/1.js console.log(1);// js/2.js console.log(2);再次訪問index.html,我們會在控制臺中看到下面的執行順序:
顯而易見,1.js被延后致至文檔解析完成后執行了,它的執行順序比body中的
<!DOCTYPE html> <html><head><title>defer & async</title><link rel="stylesheet" type="text/css" href="css/main.css"><!-- adding a 'defer' attribute, by default, the value will be 'true' --><script type="text/javascript" src="js/1.js" defer></script><script type="text/javascript" src="js/2.js" defer></script><script type="text/javascript" defer>console.log(3);</script></head><body><div class="text">Hello World</div><script type="text/javascript">document.addEventListener("DOMContentLoaded", function() {console.log('dom content loaded, ready state:', this.readyState);}, false);window.addEventListener('load', function() {console.log('window loaded, dom ready state:', document.readyState);}, false);</script></body> </html>可以看到,外聯的腳本是按照聲明順序執行的,內聯腳本并沒有遵守這個規則,另外,DOMContentLoaded和load事件依次被捕獲,DOM的狀態分別變為interactive和complete
接下來我們介紹一下async屬性,為了能夠很好的演示執行順序,我們還需要一些修改:
<!DOCTYPE html> <html><head><title>defer & async</title><link rel="stylesheet" type="text/css" href="css/main.css"><!-- adding a 'async' attribute, by default, the value is 'true' as well --><script type="text/javascript" src="js/1.js" async></script><script type="text/javascript" src="js/2.js" async></script><script type="text/javascript" src="js/3.js" async></script></head><body><div class="text">Hello World</div></body> </html>JS文件內如下:
// js/1.js console.log(1);// js/2.js console.log(2);// js/3.js console.log(3);再次訪問index.html,會發現控制臺打印如下:
我們發現,3個腳本的執行是沒有順序的,我們也無法預測每個腳本的下載和執行的時間和順序。async和defer一樣,不會阻塞當前文檔的解析,它會異步地下載腳本,但和defer不同的是,async會在腳本下載完成后立即執行,如果項目中腳本之間存在依賴關系,不推薦使用async。
關于async,也需要注意以下幾點:
1. 只適用于外聯腳本,這一點和defer一致
2. 如果有多個聲明了async的腳本,其下載和執行也是異步的,不能確保彼此的先后順序
3. async會在load事件之前執行,但并不能確保與DOMContentLoaded的執行先后順序
以上就是defer和async的介紹,了解和掌握這兩個屬性的作用,不僅對JS代碼執行的控制更加熟練,也會對頁面渲染多一分了解。
參考文章:
- http://blog.csdn.net/liuhe688/article/details/51247484
- https://segmentfault.com/q/1010000000640869
總結
以上是生活随笔為你收集整理的defer和async的原理与区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jquery事件绑定解绑机制源码分析
- 下一篇: 微信抖音社区团购小程序源码开发方案怎么做