淺談script標籤中的async和defer

 

script標籤用於加載腳本與執行腳本,在前端開發中能夠說是很是重要的標籤了。
直接使用script腳本的話,html會按照順序來加載並執行腳本,在腳本加載&執行的過程當中,會阻塞後續的DOM渲染。html

如今你們習慣於在頁面中引用各類的第三方腳本,若是第三方服務商出現了一些小問題,好比延遲之類的,就會使得頁面白屏。
好在script提供了兩種方式來解決上述問題,asyncdefer,這兩個屬性使得script都不會阻塞DOM的渲染。
但既然會存在兩個屬性,那麼就說明,這兩個屬性之間確定是有差別的。前端

defer

若是script標籤設置了該屬性,則瀏覽器會異步的下載該文件而且不會影響到後續DOM的渲染;
若是有多個設置了deferscript標籤存在,則會按照順序執行全部的script
defer腳本會在文檔渲染完畢後,DOMContentLoaded事件調用前執行。git

咱們作了一個測試頁面,頁面中包含了兩個script標籤的加載,給他們都加上defer標識。
P.S. 爲了更直觀,咱們給script1.js添加了1s的延遲,給script2.js添加了2s的延遲。
image
下圖是頁面加載的過程&script腳本的輸出順序。
不難看出,雖然script1加載用時雖然比script2短,但由於defer的限制,因此Ta只能等前邊的腳本執行完畢後才能執行。
image
imagegithub

async

async的設置,會使得script腳本異步的加載並在容許的狀況下執行
async的執行,並不會按着script在頁面中的順序來執行,而是誰先加載完誰執行。chrome

咱們修改測試頁面以下:
image
遂獲得了以下的結果,頁面加載時長上,並無什麼變化,畢竟都是異步加載的腳本。
可是咱們能夠看到一個小細節,DOMContentLoaded事件的觸發並不受async腳本加載的影響,在腳本加載完以前,就已經觸發了DOMContentLoaded
image
image
image
imagenpm

咱們接着修改測試頁面。加載一個沒有延遲的script腳本,使得腳本能夠即時的加載完畢。
咱們要測試一下,若是async腳本加載的足夠快,是否會在DOMContentLoaded以前就執行(這個實驗是基於對async的描述「在容許的狀況下執行」的論證)。
同時爲了保證測試的穩定性,咱們在script腳本引入的後邊添加了數千個空的div節點,用來延長文檔的渲染時間。
image
執行結果不出所料,若是給async必定的時間,是有可能在DOMContentLoaded事件以前就執行的。
image
P.S. 從上圖中左上角的火焰圖中,咱們也能看到,出現了多段的藍色(更新:晚上寫的時候懵了,紫色的纔是渲染,藍色的是解析)文檔渲染。以及下邊Console的順序。
說明的確,async的執行是加載完成就會去執行,而不像defer那樣要等待全部的腳本加載完後按照順序執行。瀏覽器

畫幾張圖簡要說明

網上有了很多這種相似的圖,可是基本都是拿一個script就舉例的
未免太過寒酸,so我們來一個豪華版,來畫一下多個腳本加載時的甘特圖
就像近年來各大手機廠商,出新機都喜歡來一個X+X plus異步

拿四個不一樣的顏色來標明各自表明的含義async

更正:文檔渲染 應該爲 文檔解析
imagepost

普通script

文檔解析的過程當中,若是遇到script腳本,就會中止頁面的解析進行下載(可是Chrome會作一個優化,若是遇到script腳本,會快速的查看後邊有沒有須要下載其餘資源的,若是有的話,會先下載那些資源,而後再進行下載script所對應的資源,這樣可以節省一部分下載的時間 @Update: 2018-08-17)。
資源的下載是在解析過程當中進行的,雖然說script1腳本會很快的加載完畢,可是他前邊的script2並無加載&執行,因此他只能處於一個掛起的狀態,等待script2執行完畢後再執行。
當這兩個腳本都執行完畢後,纔會繼續解析頁面。
image

defer

文檔解析時,遇到設置了defer的腳本,就會在後臺進行下載,可是並不會阻止文檔的渲染,當頁面解析&渲染完畢後。
會等到全部的defer腳本加載完畢並按照順序執行,執行完畢後會觸發DOMContentLoaded事件。
image

async

async腳本會在加載完畢後執行。
async腳本的加載不計入DOMContentLoaded事件統計,也就是說下圖兩種狀況都是有可能發生的

image
image

推薦的應用場景

defer

若是你的腳本代碼依賴於頁面中的DOM元素(文檔是否解析完畢),或者被其餘腳本文件依賴。
例:

  1. 評論框
  2. 代碼語法高亮
  3. polyfill.js

async

若是你的腳本並不關心頁面中的DOM元素(文檔是否解析完畢),而且也不會產生其餘腳本須要的數據。
例:

  1. 百度統計

若是不太能肯定的話,用defer老是會比async穩定。。。

參考資料

  1. https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script

測試代碼存放處:https://github.com/Jiasm/research-script-tag
clone後執行npm start便可運行。
調試推薦使用chrome無痕模式(這樣纔不會在Performance頁簽上看到不相關的插件數據)。

相關文章
相關標籤/搜索