头部引入不加 defer 或 async 的 script 会直接导致白屏;关键在加载策略而非物理位置。浏览器流式解析 HTML,遇无属性脚本即中断 DOM 构建、下载执行完毕才继续,造成首屏空白;尾部引入仅缓解但非根治,defer、async 和 type="module" 才是现代优化核心。

头部引入不加 defer 或 async 的 <script>,会直接导致白屏;尾部引入虽能缓解,但不是根治方案——关键在加载策略,不在物理位置。
头部引入默认阻塞渲染,白屏风险高
浏览器解析 HTML 是自上而下的流式过程。遇到没有 defer 或 async 的 <script>(尤其是带 src 的外部脚本),会立即中断 DOM 构建,发起网络请求,等下载、解析、执行全部完成才继续。此时 <body> 内容完全没开始渲染,用户看到的就是空白页面。
- 哪怕脚本只有几行
console.log,只要放在<head>里且无属性,就会卡住首屏 - 典型现象:页面长时间白屏,尤其在网络慢或脚本体积大时更明显
- 常见报错如
Cannot set properties of null,本质是document.getElementById找不到还没解析到的元素
尾部引入降低白屏概率,但仍有隐患
把 <script> 放在 </body> 前,DOM 已基本构建完毕,脚本能安全操作元素,用户也能更快看到内容。但这不等于“零白屏”或“最优”:
- 如果脚本本身体积大、执行耗时长,仍可能阻塞后续资源(如图片、字体)的加载与渲染
- 多个脚本若存在依赖关系,靠位置顺序硬排容易出错;手动轮询 DOM 就绪属于反模式
- 不符合 HTML 规范的写法(如
</body><script></script></html>)会被浏览器自动修正,但 CI/CD 或 Lint 工具可能报 warning
真正影响白屏的是加载方式,不是放哪
现代优化的核心是控制脚本何时下载、何时执行,而非机械地挪动标签位置:
立即学习“前端免费学习笔记(深入)”;
- defer:适合主业务逻辑(如 Vue/React 初始化、工具函数库)。异步下载,DOM 解析完后按顺序执行,既不阻塞渲染,又保障执行时机和依赖顺序
- async:仅用于完全独立、无 DOM 操作、不依赖其他脚本的代码(如统计 SDK、广告脚本)。一下载完就执行,不保序、不等 DOM
- type="module":天然支持 defer 行为,适合 ES Module 架构项目,可替代传统 defer 场景
- 内联脚本若需操作 DOM,必须包裹在
DOMContentLoaded回调中,或确保它出现在目标元素之后
什么情况可以放心放 head?
并非所有 head 中的脚本都危险。以下场景合理使用 defer 或模块化机制,反而更可控:
- 需要提前加载的 Polyfill(如 core-js),用
<script defer src="polyfill.js"></script> - 现代前端框架入口(如 Vite/webpack 输出的
type="module"脚本) - 纯声明式脚本(只定义函数、不执行、不操作 DOM),例如
function validateForm() { ... }



















