既然有了 defer,我们还需要像以前那样把 <script> 标签放到 <body> 的最底部吗?如果我把带 defer 的脚本放在 <head> 里,会有性能问题吗?
核心答案
不需要了。 使用 defer 属性后,把 <script> 放在 <head> 里不仅没有性能问题,反而是更优的做法。
原因:
defer脚本会并行下载,不阻塞 HTML 解析- 脚本执行会延迟到 DOM 解析完成后,但在
DOMContentLoaded事件之前 - 放在
<head>里可以让浏览器更早发现并开始下载脚本
深入解析
浏览器解析机制
传统 <script>(无 defer/async):
HTML 解析 ──▶ 遇到 script ──▶ 暂停解析 ──▶ 下载脚本 ──▶ 执行脚本 ──▶ 继续解析
defer 脚本:
HTML 解析 ────────────────────────────────────────────▶ DOM 解析完成 ──▶ 执行脚本
└──▶ 并行下载脚本 ──────────────────────────────────────────────────┘
为什么 <head> 里的 defer 更好?
| 位置 | 发现脚本时机 | 开始下载时机 |
|---|---|---|
<head> | 解析开始时 | 立即 |
<body> 底部 | 解析接近完成时 | 较晚 |
放在 <head> 里,浏览器可以在解析 HTML 的同时下载脚本,充分利用网络带宽。
常见误区
误区 1: "defer 脚本放 <head> 会阻塞渲染"
- 错误。defer 脚本的下载和 HTML 解析是并行的
误区 2: "放 <body> 底部更保险"
- 这是 defer 出现之前的最佳实践,现在已过时
- 放底部反而会延迟脚本的发现和下载
误区 3: "defer 和放底部效果一样"
- 不一样。放底部时,脚本下载要等到 HTML 解析到那里才开始
- defer 在
<head>里可以更早开始下载
defer vs async vs 传统方式
下载时机 执行时机 执行顺序
传统 script 阻塞解析 下载完立即执行 按文档顺序
async 并行下载 下载完立即执行 不保证顺序
defer 并行下载 DOM 解析完成后 按文档顺序
代码示例
<!-- ✅ 推荐:defer 脚本放在 <head> -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>页面标题</title>
<!-- 浏览器立即发现并开始下载,但不阻塞解析 -->
<script defer src="vendor.js"></script>
<script defer src="app.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- HTML 内容 -->
</body>
</html>
<!-- ❌ 过时做法:放在 body 底部 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>页面标题</title>
</head>
<body>
<!-- HTML 内容 -->
<!-- 要等 HTML 解析到这里才开始下载 -->
<script src="vendor.js"></script>
<script src="app.js"></script>
</body>
</html>
验证下载时机的方法
打开 Chrome DevTools → Network 面板,观察脚本的下载开始时间:
<head>里的 defer 脚本:在 HTML 下载初期就开始<body>底部的脚本:在 HTML 解析接近完成时才开始
面试技巧
可能的追问方向
-
"defer 和 async 有什么区别?"
- async 下载完立即执行,不保证顺序
- defer 等 DOM 解析完才执行,保证顺序
-
"多个 defer 脚本的执行顺序是怎样的?"
- 按照在文档中出现的顺序执行
- 即使后面的脚本先下载完,也会等前面的
-
"defer 脚本和 DOMContentLoaded 的关系?"
- defer 脚本在 DOM 解析完成后、DOMContentLoaded 触发前执行
-
"什么情况下还是要放 body 底部?"
- 需要兼容不支持 defer 的古老浏览器(IE9 以下)
- 现代开发中基本不需要考虑
展示深度的回答方式
"defer 放
<head>不仅没有性能问题,反而是更优的选择。因为浏览器的预加载扫描器(Preload Scanner)可以在解析 HTML 的早期就发现这些脚本并开始下载,充分利用网络带宽。而放在<body>底部的话,脚本的发现时机会延后,相当于浪费了并行下载的机会。"
一句话总结
defer 脚本放
<head>是现代最佳实践:更早发现、并行下载、不阻塞解析、按序执行。