認識 <script> 中的 async 與 defer 屬性

偶然間學到的,所以紀錄一下。

懶人包

提醒:這兩個屬性都是基於 src 引入外部資源用的,如果是 inline script 的話沒有任何作用。

首先解釋一下在沒有 async 跟 defer 時的流程:

  1. 停止解析
  2. 根據路徑發 request
  3. 等待 response
  4. 拿到 response 後,執行腳本內容
  5. 執行完畢,繼續向下解析

async

一看名字就知道是用「非同步」來載入外部資源,所以不需要等待 response

這邊舉個例子:

1
2
3
4
5
6
7
8
9
10
11
<!-- index.html -->
<!--
這邊在 sever 做了一點設定,要等 3 秒才會拿到 response
-->
<script src="http://localhost:5000/"></script>
<button>button</button>
<script>
document.querySelector('button').addEventListener('click', () => {
console.log('click')
})
</script>

因為不是非同步,所以到 <script src="http://localhost:5000/"></script> 就要等 3 秒,後面的東西都不會被執行。

no-async

加上 async 就不一樣了:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- index.html -->
<!--
一樣要等 3 秒才會拿到 response,
但現在加上 async 就不用在這裡等
-->
<script src="http://localhost:5000/" async></script>
<button>button</button>
<script>
document.querySelector('button').addEventListener('click', () => {
console.log('click')
})
</script>

async

雖然會轉圈圈但其實瀏覽器並沒有「阻塞」。

defer

顧名思義就是「延遲」,延遲什麼?把腳本延遲到 DOM 元素被解析完後再執行。(嚴謹來說是 DOMContentLoaded 以前,但我覺得知道這樣就夠了)

如果沒有延遲,一碰到 <script> 就會直接執行,這個時候如果 DOM 還沒被解析你就抓不到元素:

1
2
// getElement.js
console.log(document.querySelector('button'))
1
2
3
4
5
<body>
<!-- 到這行就直接執行 -->
<script src="./getElement.js"></script>
<button>button</button>
</body>

no-defer

加上 defer 就可以確保 DOM 元素解析完才執行:

1
2
3
4
5
<body>
<!-- 等到 DOM 解析完才執行 -->
<script src="./getElement.js" defer></script>
<button>button</button>
</body>

defer

那兩個一起用代表什麼?

不用想太複雜,就是同時套用兩個邏輯:

  1. 用非同步去發 request,不要停止解析
  2. 如果拿到資料後 DOM 還沒解析完,就等到解析完後再執行

所以簡單來說,一個是用來決定「要不要先停止解析」,另一個是決定「要什麼時候才執行腳本」。

<script> 有沒有 module 屬性的差別 week10 綜合能力測驗
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×