Skip to content

在 HTML 引入 JavaScript:外部引入、內嵌、defer 與 async

在 HTML 中引入 JavaScript 程式碼:外部引入與內嵌

JavaScript 負責網頁的行為,透過 <script> 標籤引入到 HTML 中,可以連結外部檔案也可以直接內嵌。要注意它會擋住 HTML 的解析,更多寫法可參考 JavaScript 我該寫在哪裡

外部引入

外部引入是將 JavaScript 放在一個獨立的 .js 文件中,然後通過 HTML 文件的 <script src="..."> 標籤進行引用。

html
<script src="./js/index.js"></script>

小提醒

帶有 src<script> 不能省略結束標籤,標籤內也不能再寫程式碼,寫了會被完全忽略。

內嵌

內嵌是把程式碼直接寫在該網頁中(單一網頁),其他網頁無法共用這段程式碼。

html
<script>
  // JavaScript
</script>

script 會擋住 HTML 的解析

瀏覽器由上往下解析 HTML,遇到沒有加任何屬性的 <script> 時會停下來,先把程式碼下載並執行完才繼續。<script> 放在 <head> 裡又沒有處理,使用者就會多等一段白畫面。

html
<!-- ❌ 會擋住解析,且此時還選取不到頁面上的元素 -->
<head>
  <script src="./js/index.js"></script>
</head>

defer

一邊解析 HTML 一邊在背景下載程式碼,等 HTML 解析完成後再 依照書寫順序 執行,是目前最推薦的做法:

html
<head>
  <script src="./js/index.js" defer></script>
</head>

async

一樣在背景下載,但下載完就立刻執行,不保證順序,也可能打斷解析。適合與其他程式沒有相依關係的第三方程式碼,例如:流量統計:

html
<script src="https://example.com/analytics.js" async></script>

放在 body 結尾

傳統做法是把程式碼移到 內容區 的最後面,此時 DOM 已經建立完成:

html
<body>
  ...
  <script src="./js/index.js"></script>
</body>
寫法下載時機執行時機順序
無屬性立即,會擋住解析下載完立刻執行依序
defer背景平行下載HTML 解析完成後依序
async背景平行下載下載完立刻執行不保證
放 body 結尾解析到才下載下載完立刻執行依序

樣式檔同樣會阻塞渲染(只是阻塞的是繪製而非解析),加上第三方程式碼的延後載入策略,見 阻塞渲染的 CSS 與 JavaScript

模組化的程式碼

type="module" 可以使用 importexport 語法,而且模組預設就有 defer 的行為:

html
<script type="module" src="./js/main.js"></script>

常見問題

script 該放在 head 還是 body 結尾?

兩種都可行。放在 body 結尾時,HTML 已經解析完才執行程式碼;放在 <head> 則建議加上 defer,讓瀏覽器一邊解析 HTML 一邊下載程式碼,等解析完成後再依序執行,通常會比放在結尾更快。

defer 與 async 有什麼差別?

兩者都會在背景下載程式碼。defer 會等 HTML 解析完成後、依照書寫順序執行;async 則是下載完就立刻執行,順序不保證。有相依關係的程式碼用 defer,獨立的第三方統計碼才適合 async

為什麼程式碼找不到頁面上的元素?

因為程式碼比元素更早執行。寫在 head 又沒加 defer 時,HTML 都還沒解析到那個元素,自然 選取 不到。加上 defer、把程式碼移到 body 結尾,或等待 DOMContentLoaded 事件都能解決。

有 src 的 script 裡面還可以寫程式碼嗎?

不行。當 <script> 帶有 src 屬性時,標籤內的內容會被完全忽略,因此結束標籤不能省略,內嵌的程式碼也必須另外用一個 <script> 標籤來寫。

延伸閱讀