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