async和defer仅对带src的外部脚本生效,内联脚本添加二者无效,仍立即执行并阻塞解析;ie9对defer的支持属历史特例,现代标准严格限定其仅作用于外部脚本。

async 和 defer 只对 src 外部脚本生效
这是最常被忽略的硬性限制:无论你写 <script defer>console.log(1)</script> 还是 <script async>alert('hi')</script>,浏览器都会直接忽略这些属性。内联脚本不支持 async 或 defer,它们的行为和普通内联脚本完全一致——立即阻塞解析、立即执行。
常见错误现象:document.getElementById("app") 返回 null,你以为加了 defer 就安全了,结果发现它根本没起作用,因为脚本是内联的。
- 必须用
src属性引入外部 JS 文件,async/defer才可能生效 - 哪怕只有一行代码,也得单独提成
.js文件再引用 - Webpack/Vite 构建产物默认都是外部文件,这点通常没问题;但手写 HTML 时容易踩坑
IE 浏览器的 defer 特殊行为
IE9 及更早版本会把 defer 应用到内联脚本上(属历史特例),而现代标准明确禁止这么做。IE10+ 开始才按规范只支持外部脚本的 defer,且执行时机基本符合「DOM 解析完、DOMContentLoaded 前」的预期。
但注意:IE9 对多个 defer 脚本的执行顺序并不稳定,尤其当脚本分布在 和 不同位置时,可能错乱。而 Chrome/Firefox/Safari/Edge(2026 年)均严格按文档顺序执行 defer 脚本。
- 若需兼容 IE9,不要依赖多个
defer脚本的执行顺序 - IE9 下
async不可用(仅 IE10+ 支持),别写async还指望它在旧 IE 工作 - 实际项目中,IE 已基本退出主流支持范围,但遗留系统仍需留意
同时写 async 和 defer 会发生什么
HTML 规范明确规定:当一个 <script></script> 同时包含 async 和 defer,浏览器只认 async,defer 被静默忽略。这不是 bug,是标准行为。
常见错误现象:开发者以为“双保险”更稳妥,结果脚本变成乱序执行,依赖关系崩塌——比如 lodash.js 和 utils.js 都加了 async defer,后者反而先执行了。
- 永远不要同时写两个属性,选其一即可
- 需要保序 → 用
defer - 完全独立、无 DOM 访问、无相互依赖 → 用
async - 不确定就先删掉
async,只留defer更安全
async 脚本可能晚于 window.onload 执行
这在弱网或高并发场景下真实存在:虽然规范说 async 脚本应在 window.onload 前执行,但实际中若下载慢、主线程忙,它可能卡在 load 之后才运行。此时在脚本里监听 window.onload 会失效——事件早已触发完毕。
典型问题:统计 SDK 使用 async 引入,但初始化逻辑写了 window.addEventListener('load', init),结果 init 永远不执行。
- 对
async脚本,避免依赖window.onload或DOMContentLoaded的监听时机 - 改用轮询
document.readyState或直接判断 DOM 是否就绪(如document.body存在) - 如果必须等页面加载完成,
defer是更可靠的选择
defer 在现代浏览器中已足够稳定,async 的不可控性则始终存在——它不是 bug,而是设计使然。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











