优先defer:需操作dom或存在依赖关系;优先async:完全独立、不操作dom且无需顺序执行;两者共存时async生效,defer被忽略。

选 defer 还是 async,关键看脚本要不要操作 DOM、会不会被其他脚本依赖——不是“哪个更好”,而是“哪个不崩”。
脚本要访问或修改 DOM 元素?必须用 defer
比如初始化导航菜单、绑定按钮点击事件、读取 document.getElementById 返回的节点,这些操作都要求 DOM 已就绪。用 async 时脚本可能在 都没解析完就执行,document.querySelector 返回 null 是常态。
-
defer脚本一定在 HTML 解析完成、DOMContentLoaded触发前执行,DOM 树完整可用 - 多个
defer脚本严格按 HTML 中出现顺序执行,适合先加载utils.js再加载main.js这类依赖链 - 只对带
src的外部脚本生效;写成<script defer>console.log('hi')</script>的内联脚本,defer被忽略
脚本完全独立、不碰 DOM、也不被别人依赖?优先 async
典型如统计埋点(analytics.js)、广告 SDK(ad-banner.js)、错误监控(error-tracker.js)。它们只做自己的事,越早运行越好,且不关心页面结构。
-
async下载完立刻执行,哪怕此时 HTML 解析到一半,会中断解析去跑 JS,但不阻塞下载 - 多个
async脚本谁先下完谁先跑,顺序不可控——所以不能指望script-A.js一定在script-B.js前执行 - 执行时机可能早于
DOMContentLoaded,也可能晚于,无法预测;若脚本内部有window.addEventListener('DOMContentLoaded', ...),其实没必要
两个都加?浏览器只认一个,async 优先级更高
如果同时写 <script async defer src="a.js"></script>,现代浏览器会忽略 defer,按 async 行为处理。这不是 bug,是规范明确规定的:当两者共存时,async 生效,defer 被丢弃。
- 不要试图“叠加效果”,这只会引入不确定性
- 旧版 IE(≤9)根本不支持这两个属性,会退化为同步加载,所以生产环境仍需考虑降级方案(比如把脚本放
底部) - 注意:所有带
defer或async的脚本,都不能在执行中调用document.write(),否则会清空整个页面
不写任何属性?就是在赌网络和设备运气
默认行为是同步加载:遇到 <script src="x.js"></script>,HTML 解析立刻暂停,等 JS 下载、编译、执行完才继续。首屏白屏时间直接拉长,Lighthouse 评分暴跌。
- 即使脚本很小,HTTP/1.1 下也受队头阻塞影响;HTTP/2 虽缓解,但执行阻塞仍在
- 移动端弱网下,一个未压缩的 200KB 分析脚本就能让首屏延迟 2 秒以上
- 唯一合理不加属性的场景:脚本极小(
真正容易被忽略的是:defer 和 async 都只作用于外部脚本,而且它们改变的是“执行时机”,不是“加载优先级”。如果你的脚本本身依赖另一个未声明 defer 的同步脚本,那顺序保障就彻底失效了——得从构建流程或模块依赖上解决,不是加个属性能兜住的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











