Scripts: async, defer

1、背景

瀏覽器構建DOM樹過程中遇到script標籤就會暫定構建,並開始下載腳本,執行腳本,以後再繼續構建DOM樹。
而且相對於文檔,每每腳本文件體積大,而且網絡下載的耗時比解析DOM樹要大得多。html

1.1 script標籤的影響

阻塞瀏覽器構建DOM樹(即便腳本並不會調用document.write),用戶看到白屏(嚴格說是script標籤後面的頁面部分)時間比較長。html5

1.2 最佳實踐

script標籤放到body標籤最後。git

2、defer

告訴瀏覽器能夠異步下載外部腳本,不用等待下載執行,能夠繼續構建DOM樹,等DOM樹構建完成後才執行腳本。github

下載:後臺並行下載(異步下載)
執行DOM構建後,但在DOMContentLoaded事件觸發前
多個defer按照書寫順序依次串行方式執行。
defer不會阻塞DOM樹構建,可是會阻塞DOMContentLoaded事件觸發。瀏覽器

3、async

告訴瀏覽器能夠異步下載外部腳本,不用等待下載執行,能夠繼續構建DOM樹,而且下載完成後就能夠執行了。
針對徹底獨立的腳本。不會依賴其餘腳本,同時其餘腳本也不能依賴 async腳本。網絡

下載:後臺並行下載(異步下載)
執行:下載完就執行了。無需等待其餘腳本,即先加載完的先執行。
async之間,以及和DOMContentLoaded事件處理函數執行順序是無序的。less

注意:由於JS是單線程的若是在執行async腳本,此時其餘工做就得暫定了,好比DOM樹構建(不過這種狀況不多見,除非JS腳本下載很快,而且DOM很大)異步

4、總結

下載順序 執行順序 DOMContentLoaded
async 異步並行下載,不阻塞DOM樹構建 先加載完的先執行(Load-first Order) DOMContentLoaded無關,執行順序不定
defer async DOM構建後,根據文檔順序依次串行執行 DOM樹構建後,但DOMContentLoaded事件觸發前執行

相同點async

  1. 都不會阻塞DOM樹構建,都是異步下載,可讓用戶先看到頁面內容;
  2. 都不能夠調用document.write,不然瀏覽器有個warning:
Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.

差別點函數

  1. deferasync的惟一區別就是執行時間點不一樣。

    • defer指推遲執行(執行順序不變)
    • async指異步執行(執行順序不固定)

5、動態添加的腳本行爲

5.1 經過document.createElement('script')方式添加腳本

經過document.createElement('script')方式建立的script對象,其async屬性默認爲true

let script = document.createElement('script');
console.log(script.async) // true
console.log(script.defer) // false

若是顯示地設置script.async=false,則行爲同defer,即便此時script.defer=false

5.2 經過document.write('<script src="xxxx"><\/script>')添加腳本

document.write自己的功能就是向文檔流裏插入內容,因此經過document.write('<script src="xxxx"><\/script>')添加腳本的效果同直接在文檔裏寫script標籤方式。

<script>
  document.write('<script defer src="./lib/normal.long.js"><\/script>')
</script>

<!-- 等價 -->
<script defer src="./lib/normal.long.js"><\/script>

6、Issues

6.1 同時添加async, defer的效果呢?

  1. 按照async效果執行;
  2. 由於async是HTML5才提出的,而defer是HTML4就有了,因此deferasync有更好的兼容性。同時添加async, defer的另外一個效果是若是瀏覽器不支持async,則降級爲defer
  3. 最佳實踐就是把 script標籤放到body標籤最後,防止瀏覽器不支持async,也不支持defer

參考

整理自GitHub筆記 Scripts: async, defer

相關文章
相關標籤/搜索