不加async或defer的script标签会阻塞HTML解析:浏览器遇到时立即暂停解析,下载并执行脚本后才继续,导致白屏和DOM访问失败;内联与外链脚本均如此,且按文档顺序串行执行。

不加 async 或 defer 的 script 标签会阻塞 HTML 解析
浏览器遇到普通 <script></script> 标签(无论是否写 type="text/javascript")时,会立即暂停 HTML 解析,下载(如果是外部脚本)、编译并执行脚本,之后才继续解析后续 HTML。这意味着:如果脚本放在 里且体积大,用户会看到白屏或 DOM 渲染延迟。
常见错误现象包括:document.body 为 null、getElementById 找不到元素、CSS 尚未生效就尝试读取样式等。
-
type="text/javascript"在现代 HTML5 中是冗余的,可省略;加了也不改变执行时机 - 内联脚本(无
src)和外链脚本(有src)在默认行为下都阻塞解析 - 多个无属性的
<script></script>按文档顺序串行执行,前一个没执行完,后一个不会开始
async 和 defer 对执行顺序的影响完全不同
async 和 defer 都能避免阻塞 HTML 解析,但它们对「何时执行」和「执行顺序保证」的处理差异极大,选错会导致脚本报错或逻辑错乱。
-
async:脚本下载与 HTML 解析并行,下载完成立即执行 —— 执行时机不可控,不保证顺序,适合完全独立的脚本(如统计代码、广告 SDK) -
defer:脚本下载与 HTML 解析并行,但所有defer脚本会在DOMContentLoaded事件前、按出现顺序依次执行 —— 适合依赖 DOM 或彼此有调用关系的脚本(如 Vue + 插件、jQuery + 自定义模块) - 两者都不能用于内联脚本(
<script>console.log(1)</script>)的加载优化;async对内联脚本无效,defer虽被忽略但无害
动态插入的 script 默认表现类似 async
用 JavaScript 创建并插入的 <script></script> 元素(例如 document.createElement('script')),其行为等价于带 async 属性的外链脚本:下载不阻塞,执行不保序,且无法通过 defer 改变。
这意味着:
- 如果你需要确保动态脚本在 DOM 构建完成后执行,得手动监听
DOMContentLoaded或用setTimeout(..., 0)延迟一小段再插入 - 多个动态插入的脚本之间没有执行顺序保障,不能假设
a.js一定比b.js先执行,即使先调用appendChild -
document.currentScript在动态脚本中可能为null(尤其在异步上下文中),别依赖它做路径推断
type 属性不是执行控制开关,别把它当 defer/async 用
type 属性只影响浏览器如何解释脚本内容,不影响加载和执行时机。比如 type="module" 会自动启用 defer 行为,但 type="text/javascript" 或空值都不会带来任何加载策略变化。
容易被忽略的点:
- IE 不支持
type="module",若混用type="module"和传统脚本,需用nomodule降级 - 某些构建工具(如 Vite)会把
type="application/json"当作数据块注入,但它根本不会执行 —— 这类用法和 JS 执行顺序无关 - 写
type="text/javascript"只会让代码显得过时,还可能在严格 MIME 检查环境下触发警告(如某些 CDN 或 CSP 策略)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











