defer 屬性和 async 屬性

ladybug7發表於2024-06-09

defer 屬性

為了解決指令碼檔案下載阻塞網頁渲染的問題,一個方法是對 <script> 元素加入 defer 屬性。它的作用是延遲指令碼的執行,等到 DOM 載入生成後,再執行指令碼。

<script src="a.js" defer></script>
<script src="b.js" defer></script>

上面程式碼中,只有等到 DOM 載入完成後,才會執行 a.jsb.js

defer 屬性的執行流程如下。

  1. 瀏覽器開始解析 HTML 網頁。
  2. 解析過程中,發現帶有 defer 屬性的 <script> 元素。
  3. 瀏覽器繼續往下解析 HTML 網頁,同時並行下載 <script> 元素載入的外部指令碼。
  4. 瀏覽器完成解析 HTML 網頁,此時再回過頭執行已經下載完成的指令碼。

有了 defer 屬性,瀏覽器下載指令碼檔案的時候,不會阻塞頁面渲染。下載的指令碼檔案 DOMContentLoaded 事件觸發前執行(即剛剛讀取完 </html> 標籤),而且可以保證執行順序就是它們在頁面上出現的順序。

對於內建而不是載入外部指令碼的 script 標籤,以及動態生成的 script 標籤,defer 屬性不起作用。另外,使用 defer 載入的外部指令碼不應該使用 document.write 方法。

async 屬性

解決“阻塞效應”的另一個方法是對 <script> 元素加入 async 屬性。

<script src="a.js" async></script>
<script src="b.js" async></script>

async 屬性的作用是,使用另一個程序下載指令碼,下載時不會阻塞渲染。

  1. 瀏覽器開始解析 HTML 網頁。
  2. 解析過程中,發現帶有 async 屬性的 script 標籤。
  3. 瀏覽器繼續往下解析 HTML 網頁,同時並行下載 <script> 標籤中的外部指令碼。
  4. 指令碼下載完成,瀏覽器暫停解析 HTML 網頁,開始執行下載的指令碼。
  5. 指令碼執行完畢,瀏覽器恢復解析 HTML 網頁。

async 屬性可以保證指令碼下載的同時,瀏覽器繼續渲染。需要注意的是,一旦採用這個屬性,就無法保證指令碼的執行順序。哪個指令碼先下載結束,就先執行那個指令碼。另外,使用 async 屬性的指令碼檔案裡面的程式碼,不應該使用 document.write 方法。

defer 屬性和 async 屬性到底應該使用哪一個?

一般來說,如果指令碼之間沒有依賴關係,就使用 async 屬性,如果指令碼之間有依賴關係,就使用 defer 屬性。如果同時使用 asyncdefer 屬性,後者不起作用,瀏覽器行為有 async 屬性決定。

相關文章