必须用defer:脚本需操作dom或依赖其他脚本;可放心用async:脚本完全独立、不操作dom、不被依赖。二者均只对带src的外部脚本生效,同时写时浏览器仅执行async。

该用 async 还是 defer,不看“要不要延迟”,只盯两个事实:脚本是否需要操作 DOM,以及是否依赖其他脚本的执行结果。错选一个,document.getElementById 就返回 null,或者 jQuery is not defined 直接报错。
什么时候必须用 defer
当脚本要读写 DOM 元素、或它和别的外部脚本有明确先后依赖(比如先加载 vue.js,再执行 app.js),defer 是唯一安全选项。
-
defer脚本一定在DOMContentLoaded之前执行,此时整个 DOM 树已就绪,querySelector、addEventListener都能放心调用 - 多个
defer脚本严格按 HTML 中的书写顺序执行,哪怕b.js比a.js下载快,也一定等a.js执行完才轮到b.js - 注意:
defer对没有src的内联脚本无效,浏览器会直接忽略该属性
什么时候可以放心用 async
async 只适合完全独立、不碰 DOM、也不被其他脚本依赖的脚本,典型如埋点统计、广告 SDK、A/B 测试工具。
-
async脚本一下载完就执行,可能发生在 HTML 解析中途,此时#header元素还没生成,document.getElementById('header')必然返回null - 多个
async脚本之间无执行顺序保障,analytics.js和ads.js谁先执行完全由网络速度决定 - 如果脚本里写了
console.log(document.body),在async下大概率输出null,因为执行时标签可能还没解析到
常见错误现象与排查线索
页面初始化失败、DOM 操作报错、第三方库未定义——这些问题八成出在 async/defer 误配。
- 报错
TypeError: Cannot read property 'addEventListener' of null→ 脚本用了async却尝试绑定 DOM 元素,应换defer或加DOMContentLoaded包裹 - 报错
ReferenceError: $ is not defined→ jQuery 加了async,而后续插件脚本没加或加了但执行更早,应统一改用defer - 埋点数据漏发或重复上报 → 统计脚本用了
defer导致太晚触发,可改async;若需确保 DOM 稳定后再上报,保留defer更稳妥 - 服务端渲染(SSR)页面白屏时间变长 → 把本该
defer的初始化逻辑写进了同步脚本,应外置并加defer
兼容性与特殊限制
async 和 defer 都只对带 src 的外部脚本生效,这是最容易被忽略的硬约束。
- 同时写
async defer,浏览器按规范只认async行为,defer被静默忽略 - IE10+ 支持
defer,IE9 及以下仅支持defer(且对内联脚本也生效,属历史特例);async需 IE10+,现代浏览器均无问题 - 动态插入的脚本(
document.createElement('script'))默认行为类似async,如需保序或等 DOM,得手动监听DOMContentLoaded或用load事件控制
真正复杂的地方不在属性本身,而在脚本之间的隐式依赖:某个看似独立的 async 脚本,可能悄悄读取了另一个脚本挂载到 window 上的变量。这种依赖不会报错,但行为不可控——这时候,defer 不是“更慢的选择”,而是让不确定性落地的唯一方式。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











