文章中英模式
常見的前端面試題目 - 頁面載入 - Script 位置與 defer、async 屬性
深入解析 JavaScript Script 標籤的最佳放置位置,以及 defer 與 async 屬性的使用時機與效果,幫助你提升網頁效能與用戶體驗。
文章中英模式

懶得看文章?那就來看影片吧
Script 標籤的位置
網頁中 JavaScript 的載入與執行時機直接影響使用者體驗。根據擺放位置的不同,script 標籤的行為也有所差異:
- 1.放在
<head>中:瀏覽器會在解析 HTML 前先下載並執行 JavaScript,這會阻塞頁面渲染,導致頁面載入速度變慢。 - 2.放在
</body>前:瀏覽器會先渲染完 HTML 內容,再下載並執行 JavaScript,用戶可以更快看到頁面內容,但可能在 JavaScript 載入前無法使用某些功能。
defer 屬性的用途
defer 屬性讓腳本的下載與 HTML 解析同時進行,但會延遲執行直到 HTML 解析完成:
<script defer src="script.js"></script>defer 的特點:
- •不會阻塞 HTML 解析與渲染
- •保證按照在 HTML 中的順序執行
- •在 DOMContentLoaded 事件之前執行
- •僅對外部腳本(有 src 屬性)有效
適用場景:
- •需要 DOM 完整載入後才能執行的腳本
- •依賴於其他腳本順序的執行
- •不急需立即執行的腳本
module 腳本的用途
type="module" 讓瀏覽器將腳本視為 JavaScript 模組,支持 import/export 語法:
<script type="module" src="app.js"></script>module 與 defer 的區別:
- •module 腳本預設具有 defer 行為,無需額外添加 defer 屬性
- •module 支持 import/export 語法,而 defer 腳本不支持
- •module 腳本自動採用嚴格模式 (strict mode)
必須使用 module 而非 defer 的場景:
- •需要使用 import/export 語法時
- •需要模組化程式碼以提高可維護性
// math.js (模組)
export function add(a, b) {
return a + b;
}
// app.js (使用模組)
import { add } from './math.js';
document.addEventListener('DOMContentLoaded', () => {
console.log(add(2, 3)); // 輸出: 5
});async 屬性的用途
async 屬性讓腳本的下載與 HTML 解析同時進行,下載完成後立即執行:
<script async src="script.js"></script>async 的特點:
- •不會阻塞 HTML 解析與渲染
- •不保證執行順序,先下載完的先執行
- •執行時會暫停 HTML 解析
- •僅對外部腳本有效
- •不一定在 DOMContentLoaded 事件前執行
適用場景:
- •獨立的、不依賴其他腳本的功能
- •分析腳本、廣告腳本等第三方腳本
- •不需要操作 DOM 的腳本
圖解比較
以下圖解展示了不同腳本載入方式的行為差異:
──────────────────────────── 時間軸 ────────────────────────────>
【無屬性 - 放在 </body> 前】
HTML 解析: ■■■■■■■■■■■■■■■■■■■■■■■■■■HTML解析■■■■■■■■■■■■■■■■■■
↑ ↑
開始解析 解析完成
腳本處理: ■■■下載+執行■■■
↑ ↑
開始下載 執行完成
<body>
<!-- 內容 -->
<script src="script.js"></script> <!-- 傳統做法 -->
</body>
--------------------------------
【defer】
HTML 解析: ■■■■■■■■■■■■■■■■■■■■■■■■■■HTML解析■■■■■■■■■■■■■■■■■■→DOM就緒
↑ ↑ ↑
開始解析 解析完成 DOMContentLoaded
腳本處理: ■■■■■■■■■■■■■下載■■■■■■■■■■■■■ ■■■執行■■■
↑ ↑ ↑ ↑
開始下載 下載完成 開始執行 執行完成
<head>
<script defer src="script.js"></script> <!-- 現代推薦做法 -->
</head>
--------------------------------
【async】
HTML 解析: ■■■■■■■■■■■■■■■■■暫停■■■■■■■■■剩餘HTML解析■■■■■■■■■■■
↑ ↑ ↑ ↑
開始解析 暫停 繼續解析 解析完成
腳本處理: ■■■■■■■下載■■■■■■■■■執行■■■
↑ ↑ ↑
開始下載 下載完成 執行完成
<head>
<script async src="script.js"></script> <!-- 適用於獨立腳本 -->
</head>最佳實踐
- 1.現代推薦做法:將帶有
defer屬性的腳本放在<head>中<head> <script defer src="main.js"></script> </head>這樣可以盡早開始下載腳本,同時不阻塞 HTML 解析,提升頁面載入體驗。
- 2.獨立腳本:使用
async屬性<head> <script async src="analytics.js"></script> </head> - 3.傳統做法:將腳本放在
</body>前<body> <!-- 頁面內容 --> <script src="main.js"></script> </body> - 4.核心功能且時間敏感:不使用屬性,直接放在
<head>中<head> <script src="critical.js"></script> </head>
🔥 常見面試題目
(一)defer 和 async 有什麼區別?
解答:兩者主要區別在於執行時機和順序:
- •
defer:在 HTML 解析完成後按照在 HTML 中的順序執行 - •
async:下載完成後立即執行,不保證順序,會中斷 HTML 解析
<!-- defer 範例:按順序執行 -->
<head>
<script defer src="first.js"></script>
<script defer src="second.js"></script>
<!-- first.js 一定會在 second.js 之前執行 -->
</head>
<!-- async 範例:不保證順序 -->
<head>
<script async src="analytics.js"></script>
<script async src="ads.js"></script>
<!-- 哪個先下載完成就先執行哪個 -->
</head>(二)為什麼不建議將所有腳本都放在 <head> 中且不使用 defer 或 async?
解答:這會造成瀏覽器必須先下載並執行所有 JavaScript 才開始渲染頁面,導致用戶看到白屏時間延長,嚴重影響首次內容繪製(FCP)和用戶體驗。
<!-- 不推薦:會阻塞渲染 -->
<head>
<script src="large-library.js"></script>
<script src="app.js"></script>
<script src="components.js"></script>
<!-- 頁面會等待所有腳本下載和執行完才開始渲染 -->
</head>
<!-- 推薦:使用 defer -->
<head>
<script defer src="large-library.js"></script>
<script defer src="app.js"></script>
<script defer src="components.js"></script>
<!-- HTML 解析不會被阻塞,腳本會在 DOMContentLoaded 前執行 -->
</head>(三)何時選擇 defer,何時選擇 async?
解答:
- •選擇
defer:當腳本需要操作 DOM 或依賴其他腳本的執行順序 - •選擇
async:當腳本完全獨立,不需要保證順序,如分析工具、廣告等
<!-- 使用 defer 的情境:需要 DOM 和執行順序 -->
<head>
<script defer src="jquery.js"></script>
<script defer src="jquery-plugin.js"></script>
<script defer src="app.js">
// 這個腳本依賴 jquery 和 jquery-plugin
// 且可能需要操作 DOM 元素
</script>
</head>
<!-- 使用 async 的情境:獨立功能 -->
<head>
<script async src="google-analytics.js"></script>
<script async src="facebook-pixel.js"></script>
<!-- 這些腳本彼此獨立,不需要特定順序,也不依賴 DOM -->
</head>(四)module 腳本(type="module")的載入行為如何?
解答:module 腳本預設就具有 defer 的行為,即使沒有顯式設定 defer 屬性。如果想要 async 行為,需要明確添加 async 屬性。
<!-- 預設具有 defer 行為 -->
<head>
<script type="module" src="app.js"></script>
<!-- 等同於 <script type="module" defer src="app.js"></script> -->
<!-- 如果需要 async 行為,必須明確指定 -->
<script type="module" async src="independent-module.js"></script>
</head>(五)inline script 可以使用 defer 或 async 嗎?
解答:不可以。defer 和 async 屬性僅對外部腳本(有 src 屬性的 script 標籤)有效,對內聯腳本沒有效果。
<!-- 無效:defer 和 async 對內聯腳本無效 -->
<head>
<script defer>
console.log("這個 defer 屬性不起作用");
// 這個腳本會立即執行,阻塞 HTML 解析
</script>
<script async>
console.log("這個 async 屬性不起作用");
// 這個腳本也會立即執行
</script>
<!-- 有效:外部腳本可以使用 defer 或 async -->
<script defer src="external.js"></script>
</head>