是的,不加async或defer的script会强制阻塞html解析和首屏渲染;浏览器遇此脚本即暂停dom构建,下载、解析、执行完毕才继续,导致白屏或dom访问失败,无论其位于head还是body底部。

Script标签不加async或defer,就一定会阻塞HTML解析和首屏渲染——这不是优化建议,是浏览器规范强制行为。
为什么同步<script></script>会卡住首屏
浏览器解析HTML是单线程线性过程。遇到<script src="main.js"></script>(无属性),立刻暂停DOM构建,下载、解析、执行脚本后才继续。此时哪怕页面只写了<h1>欢迎</h1>,用户看到的也是白屏。
常见错误现象:
-
DOMContentLoaded时间比HTML加载完成晚800ms以上,但Network里脚本只有60KB——大概率是没加defer的同步加载拖累了整个DOM构建 - 首屏文字迟迟不出现,控制台无报错——实际是
<script></script>放在里且无属性,阻塞了后续HTML解析 -
document.getElementById("app")在内联脚本中返回null,因为对应元素还没被解析到
defer适合依赖完整DOM的业务逻辑
defer让脚本并行下载、不阻塞HTML解析,但推迟执行到整个DOM构建完成之后、DOMContentLoaded触发之前。它严格按HTML中书写顺序执行。
使用场景:
- Vue/React初始化(如
createApp)、轮播图init、表单校验等必须操作真实DOM节点的代码 - 多个有依赖关系的外链脚本,比如
utils.js必须在app.js之前执行
注意点:
-
defer对内联脚本无效:<script defer>console.log('hi')</script>和不加一样,仍同步执行 - 如果某个
defer脚本加载极慢,DOMContentLoaded会被拖住——它等所有defer脚本下载+执行完毕才触发 - 多个
defer脚本不能跨拆开写,否则顺序可能因解析位置错乱
async适合无依赖、可随时执行的统计类脚本
async让脚本下载与HTML解析完全并行,但一旦下载完成,就立即中断当前解析、执行脚本——不等其他脚本,也不等DOM就绪。
适用情况:
- 埋点、广告、监控类脚本(如
analytics.js) - 脚本自身不操作DOM,或已自行判断
document.readyState再执行
风险提示:
-
async脚本谁先下完谁先执行,顺序不可控。若它依赖另一个尚未执行的工具函数,会报xxx is not defined - 它可能在
DOMContentLoaded前执行,也可能在之后,无法保证时机 - 同样只对外链脚本生效;内联脚本加
async无意义
type="module"默认等价于defer但更现代
ES模块脚本(<script type="module" src="app.js"></script>)默认具有defer行为:并行下载、DOM构建完按序执行,同时支持import/export语法和顶层await。
优势:
- 天然避免全局变量污染
- 自动去重:同一模块URL多次
import只执行一次 - 支持
nomodule降级 fallback
注意:
- 旧版IE完全不支持,需搭配
<script nomodule></script>提供传统打包版本 - 模块路径必须是相对或绝对URL,不能是纯文件名(如
app.js会报404)
真正容易被忽略的是:哪怕你把<script></script>挪到底部,只要没加async或defer,它依然会阻塞后续渲染任务(比如paint)。所谓“放到底部就安全”,只是把阻塞从白屏阶段延后到了内容可见之后——用户可能看到文字但无样式、无交互,一样感知卡顿。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











