1. 程式人生 > 其它 >script 標籤中的defer 和 async 屬性

script 標籤中的defer 和 async 屬性

瀏覽器在解析 HTML 的時候,如果遇到一個沒有任何屬性的 <script> 標籤 ,就會暫停解析,先發送網路請求獲取該 JS 指令碼的程式碼內容,然後讓 JS 引擎執行該程式碼,當代碼執行完畢後恢復解析。整個過程如下圖所示:

可以看到,script 阻塞了瀏覽器對 HTML 的解析,如果獲取 JS 指令碼的網路請求遲遲得不到響應,或者 JS 指令碼執行時間過長,都會導致白屏,使用者看不到頁面內容。

解決方式如下:

defer 和 async 都是 非同步 載入 外部 的JS指令碼檔案,都不會阻塞頁面的解析,因此使用者可在指令碼載入前看到頁面內容。

1 async 

async 表示非同步。當瀏覽器遇到帶有 async 屬性的 script 時,請求該指令碼的網路請求是非同步的,不會阻塞瀏覽器解析 HTML,一旦網路請求回來之後,如果此時 HTML 還沒有解析完,瀏覽器會暫停解析,先讓 JS 引擎執行程式碼,執行完畢後再進行解析。

圖示如下: 

當然,如果在 JS 指令碼請求回來之前,HTML 已經解析完畢了,那就啥事沒有,立即執行 JS 程式碼,如下圖所示:

所以 async 是不可控的,因為執行時間不確定,你如果在非同步 JS 指令碼中獲取某個 DOM 元素,有可能獲取到也有可能獲取不到。而且如果存在多個 async 的時候,它們之間的執行順序也不確定,完全依賴於網路傳輸結果,誰先到執行誰。

2 defer

defer 表示延遲。當瀏覽器遇到帶有 defer 屬性的 script 時,獲取該指令碼的網路請求也是非同步的,不會阻塞瀏覽器解析 HTML,一旦網路請求回來之後,如果此時 HTML 還沒有解析完,瀏覽器不會暫停解析並執行 JS 程式碼,而是等待 HTML 解析完畢再執行 JS 程式碼。

圖示如下:

如果存在多個 defer script 標籤,瀏覽器(IE9及以下除外)會保證它們按照在 HTML 中出現的順序執行,不會破壞 JS 指令碼之間的依賴關係。

最後,根據上面的分析,不同型別 script 的執行順序及其是否阻塞解析 HTML 總結如下:

  • async,載入和渲染後續文件元素的過程將和 script.js 的載入與執行並行進行(非同步)。

  • defer,載入後續文件元素的過程將和 script.js 的載入並行進行(非同步),但 script.js 的執行要在所有元素解析完成之後,DOMContentLoaded 事件觸發之前完成。

(1)defer 是不會阻塞html解析的,它是等DOM載入完之後再去執行JavaScript程式碼;async是JavaScript下載完成,就會立即執行程式碼,等待執行完之後才繼續解析HTML。
(2)在載入多個JS指令碼的時候,async是無順序的載入,而defer是有順序的載入。