在 HTML 引入 JavaScript:外部引入、內嵌、defer 與 async
JavaScript 負責網頁的行為,透過 <script> 標籤引入到 HTML 中,可以連結外部檔案也可以直接內嵌。要注意它會擋住 HTML 的解析,更多寫法可參考 JavaScript 我該寫在哪裡。
外部引入
外部引入是將 JavaScript 放在一個獨立的 .js 文件中,然後通過 HTML 文件的 <script src="..."> 標籤進行引用。
<script src="./js/index.js"></script>小提醒
帶有 src 的 <script> 不能省略結束標籤,標籤內也不能再寫程式碼,寫了會被完全忽略。
內嵌
內嵌是把程式碼直接寫在該網頁中(單一網頁),其他網頁無法共用這段程式碼。
<script>
// JavaScript
</script>script 會擋住 HTML 的解析
瀏覽器由上往下解析 HTML,遇到沒有加任何屬性的 <script> 時會停下來,先把程式碼下載並執行完才繼續。<script> 放在 <head> 裡又沒有處理,使用者就會多等一段白畫面。
<!-- ❌ 會擋住解析,且此時還選取不到頁面上的元素 -->
<head>
<script src="./js/index.js"></script>
</head>defer
一邊解析 HTML 一邊在背景下載程式碼,等 HTML 解析完成後再 依照書寫順序 執行,是目前最推薦的做法:
<head>
<script src="./js/index.js" defer></script>
</head>async
一樣在背景下載,但下載完就立刻執行,不保證順序,也可能打斷解析。適合與其他程式沒有相依關係的第三方程式碼,例如:流量統計:
<script src="https://example.com/analytics.js" async></script>放在 body 結尾
傳統做法是把程式碼移到 內容區 的最後面,此時 DOM 已經建立完成:
<body>
...
<script src="./js/index.js"></script>
</body>| 寫法 | 下載時機 | 執行時機 | 順序 |
|---|---|---|---|
| 無屬性 | 立即,會擋住解析 | 下載完立刻執行 | 依序 |
defer | 背景平行下載 | HTML 解析完成後 | 依序 |
async | 背景平行下載 | 下載完立刻執行 | 不保證 |
| 放 body 結尾 | 解析到才下載 | 下載完立刻執行 | 依序 |
樣式檔同樣會阻塞渲染(只是阻塞的是繪製而非解析),加上第三方程式碼的延後載入策略,見 阻塞渲染的 CSS 與 JavaScript。
模組化的程式碼
type="module" 可以使用 import 與 export 語法,而且模組預設就有 defer 的行為:
<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> 標籤來寫。