JavaScript 寫在哪裡?script 標籤與 defer、async 差異
JavaScript 可以寫在哪些地方呢?其實,可以寫在任何地方。當然,造成後續的狀況也會不一樣。基本上,依狀況不同,我們可寫在三個地方,如下:
- 單一文件。
- 標籤內。
- 外部引入。
小提醒
瀏覽器遇到 <script> 標籤時,會停止渲染 HTML 文件,先執行 <script> 裡面的 JavaScript 程式碼,等到執行完畢後,才會繼續渲染接下來的 HTML 文件。
因此,早期年代常會將 <script> 標籤擺放在 </body> 前面,以確保所有 DOM 元素都已建立完成,再進行 JavaScript 的下載、執行。
單一文件
在 <head> 或 <body> 中產生 <script> 標籤區域並寫在該標籤當中。
<script>
console.log("寫在這裡");
</script>這種寫法最快,寫個小範例、驗證一段語法很方便;缺點是程式碼與頁面綁在一起,多個頁面要共用同一段邏輯時只能複製貼上,也沒辦法被瀏覽器快取。
標籤內
在標籤的屬性中,例如:onload、onclick 這類 on 開頭的屬性。
<body onload="console.log('寫在這裡')">
...
</body>注意
這種寫法就是 DOM Level 0 的事件綁定方式,實務上已不建議使用:HTML 與程式邏輯綁得太死、一個事件只能綁一個函式,也容易被植入惡意程式碼。改用 addEventListener() 來 綁定事件 才是現在的做法。
外部引入
在 <head> 或 <body> 中以 src 屬性引入外部的 .js 檔案。
<script src="檔案名稱.js"></script>這是實務上的主要寫法:同一份檔案能被多個頁面共用、可以被瀏覽器快取,也方便交給版本控制與打包工具處理。
小提醒
有 src 屬性時,<script> 標籤內部就不要再寫程式碼,裡面的內容會被忽略。要兩種都用,請分成兩個 <script> 標籤。
使用 defer、async 加速網頁載入
使用 defer、async 屬性可以加速網頁載入時間。當瀏覽器在解析 HTML 時,順序是由上而下、由左而右。因此,使用這兩個屬性,於下載時並不會阻擋 HTML 的 DOM 渲染。
<script defer src="檔案名稱.js"></script>
<script async src="檔案名稱.js"></script>但這兩個屬性又有什麼差別呢?
defer
- 整個網頁都渲染完畢,也就是 DOM 都建立完成之後才會開始執行 JavaScript。
- 會依照 HTML 的順序執行,如果已下載完畢也會等前面的 JavaScript 執行完畢才開始執行自己的 JavaScript。
async
- 不會等 DOM 建立完成,只要下載完畢就直接執行 JavaScript。
- 不會依照 HTML 的順序執行,誰先下載完誰先執行。
三種載入方式的比較
| 寫法 | 下載時是否阻擋解析 | 執行時機 | 是否保證順序 |
|---|---|---|---|
<script> | 會 | 立刻執行 | 是 |
<script defer> | 不會 | HTML 解析完成後 | 是 |
<script async> | 不會 | 下載完就執行 | 否 |
該選哪一個
- 需要操作 DOM,或多支檔案之間有依賴關係 → 用
defer。 - 完全獨立、與頁面其他程式無關的腳本,例如:流量分析 → 可以用
async。 - 兩個屬性都寫時,
async優先生效;瀏覽器不支援時才退回defer。
載入流程對照
| 方式 | 流程 |
|---|---|
| 傳統 | 畫面渲染 → 停止渲染 → 請求 JavaScript → 等待 → 執行 → 繼續渲染 |
defer | 畫面渲染(同時下載 JavaScript)→ 渲染完成 → 執行 |
async | 畫面渲染(同時下載 JavaScript)→ 下載完成即停止渲染並執行 → 繼續渲染 |
常見問題
JavaScript 應該寫在 head 還是 body 結尾?
現在建議寫在 <head> 並加上 defer。早期把 <script> 放在 </body> 前是為了確保 DOM 已建立,而 defer 同樣能保證這件事,還能讓瀏覽器提早開始下載檔案。
defer 與 async 有什麼差別?
兩者的下載都不會阻擋 HTML 解析。defer 會等 HTML 解析完成才執行,並保證依照文件中的順序執行;async 則是下載完就立刻執行,順序不固定,執行時仍會暫停解析。
為什麼 script 標籤會讓網頁變慢?
瀏覽器解析 HTML 遇到沒有 defer、async 的 <script> 時,必須先下載並執行完裡面的程式碼才會繼續解析,這段等待時間畫面就停在原地,也就是所謂的 阻塞渲染。
可以把 JavaScript 寫在 HTML 的屬性裡嗎?
語法上可以,例如:onclick 屬性,但不建議。這種寫法讓 HTML 與程式邏輯綁死、一個事件只能綁一個函式,也較容易成為被注入惡意程式碼的破口,細節可參考 DOM 事件級別。
什麼情況適合用 async?
適合與頁面其他程式無依賴關係的獨立腳本,例如:流量分析、廣告追蹤。只要腳本之間有先後依賴,或需要操作 DOM,就改用 defer。